加载中
加载中
表情图片
评为精选
鼓励
加载中...
分享
加载中...
文件下载
加载中...
修改排序
加载中...
C语言中字符串的原地插入、替换、删除
acmilan2015/10/09软件综合 IP:四川
为了节省内存空间,避免复制来复制去,我们一般喜欢在原地对字符串进行操作。其中最常见的无非是插入、替换、删除。这三个操作有个特点,都可能使字符串长度发生变化。为了适应这种变化,我们在进行这些操作之前,要调整字符串长度。原地操作字符串,最需要注意的一点,是缓冲区长度一定要足够,比如:
Other
// 这样分配的字符串可以增长到199个字符 char str1[200] = "I am a teacher.";                          // 这样分配的字符串无法增长,只能缩短 char str1[] = "I am a teacher.";

在C语言中调整字符串长度,一般用memmove移动内存。它和memcpy不同点在于它允许内存区域重叠。调整字符串长度时要同时移动尾部null字符。调整完字符后,一般需要将要替换为的字符串复制到目标位置,此时也可以使用memmove(当然memcpy也可以),但是并不复制尾部null字符。如果是宽字符的话,wmemmove比较方便,它以宽字符个数而不是字节为单位。

为方便起见,定义strmove和wcsmove函数,其中最后一个参数tz=1则复制尾部null字符,tz=0则不复制尾部null字符:
C++
char *strmove(char *ptr2, const char *ptr1, int tz) {     return (char*)memmove(ptr2, ptr1, strlen(ptr1) + tz); }                          wchar_t *wcsmove(wchar_t *ptr2, const wchar_t *ptr1, int tz) {     return wmemmove(ptr2, ptr1, wcslen(ptr1) + tz); }

接着我们可以写出strrepl和wcsrepl函数,它可以将某个子串替换为另一个子串:
C++
char *strrepl(char *repl_pos, rsize_t repl_sz, const char *src) {     if (repl_sz > strlen(repl_pos)) repl_sz = strlen(repl_pos);     strmove(repl_pos + strlen(src), repl_pos + repl_sz, 1);     strmove(repl_pos, src, 0);     return repl_pos + strlen(src); }                          wchar_t *wcsrepl(wchar_t *repl_pos, rsize_t repl_sz, const wchar_t *src) {     if (repl_sz > wcslen(repl_pos)) repl_sz = wcslen(repl_pos);     wcsmove(repl_pos + wcslen(src), repl_pos + repl_sz, 1);     wcsmove(repl_pos, src, 0);     return repl_pos + wcslen(src); }

这两个函数的使用方法相同。第一个参数repl_pos是被替换的地址,第二个参数repl_sz是被替换的长度,第三个参数src则是要替换为的字符串。

首先我们先定义两个字符串,内容是『I am a teacher.』,不同的是一个使用char[],一个使用wchar_t[]:
Other
// str1是char[]字符串 char str1[200] = "I am a teacher.";                          // str2是wchar_t[]字符串 wchar_t str1[200] = L"I am a teacher.";

插入子串的话很简单,repl_pos是要插入的位置,repl_sz=0,src=要插入的字符串:
Other
// str1是char[]字符串 strrepl(str1 + 7, 0, "good ");                          // str2是wchar_t[]字符串 wcsrepl(str2 + 7, 0, L"good ");
插入子串后的字符串『I am a good teacher.』。

替换子串的话,先用strstr和wcsstr找到要替换的地址,再用strlen和wcslen计算替换前长度,最后调用strrepl和wcsrepl:
C++
// str1是char[]字符串 char *repl_pos1 = strstr(str1, "teacher"); if (repl_pos1 != NULL) {     strrepl(repl_pos1, strlen("teacher"), "professor"); }                          // str2是wchar_t[]字符串 wchar_t *repl_pos2 = wcsstr(str2, L"teacher"); if (repl_pos2 != NULL) {     wcsrepl(repl_pos2, wcslen(L"teacher"), L"professor"); }
替换子串后的字符串『I am a good professor.』。

删除子串和替换子串类似,把src改成空字符串就可以了,然而更高效的方法是直接使用strmove或wcsmove:
C++
// str1是char[]字符串 char *del_pos1 = strstr(str1, "I am "); if (del_pos1 != NULL) {     //strrepl(del_pos, strlen("I am "), "");     strmove(del_pos1, del_pos1 + strlen("I am "), 1); }                          // str2是wchar_t[]字符串 wchar_t *del_pos2 = wcsstr(str2, L"I am "); if (del_pos2 != NULL) {     //wcsrepl(del_pos2, wcslen(L"I am "), L"");     wcsmove(del_pos2, del_pos2 + wcslen(L"I am "), 1); }
删除子串后的字符串『a good professor.』。

-----------------------------------------------------------------------------

这里用到了一个小技巧:Visual C++ 2005以上版本中,C运行库支持Unicode模式的控制台。使用_setmode将stdin、stdout、stderr的模式设为_O_U16TEXT可以将控制台设为Unicode模式。在Unicode模式时,只有wprintf、wscanf、fwputs、fwgets等宽字符I/O函数起作用。将它们的模式设为_O_TEXT可以切换回普通模式。

完整程序:
Other
#include <fcntl.h> // _O_U16TEXT (unicode console) #include <io.h> // _setmode (unicode console)    #include <stdio.h> // wprintf #include <string.h> // memmove, strlen, strstr #include <wchar.h> // wmemmove, wcslen, wcsstr    char *strmove(char *ptr2, const char *ptr1, int tz) {     return (char*)memmove(ptr2, ptr1, strlen(ptr1) + tz); }    char *strrepl(char *repl_pos, rsize_t repl_sz, const char *src) {     if (repl_sz > strlen(repl_pos)) repl_sz = strlen(repl_pos);     strmove(repl_pos + strlen(src), repl_pos + repl_sz, 1);     strmove(repl_pos, src, 0);     return repl_pos + strlen(src); }    wchar_t *wcsmove(wchar_t *ptr2, const wchar_t *ptr1, int tz) {     return wmemmove(ptr2, ptr1, wcslen(ptr1) + tz); }    wchar_t *wcsrepl(wchar_t *repl_pos, rsize_t repl_sz, const wchar_t *src) {     if (repl_sz > wcslen(repl_pos)) repl_sz = wcslen(repl_pos);     wcsmove(repl_pos + wcslen(src), repl_pos + repl_sz, 1);     wcsmove(repl_pos, src, 0);     return repl_pos + wcslen(src); }    void narrow_replace(void) {     char str1[200] = "I am a teacher.";        printf("%s\n", str1);        strrepl(str1 + 7, 0, "good ");        printf("%s\n", str1);        char *repl_pos1 = strstr(str1, "teacher");     if (repl_pos1 != NULL)     {         strrepl(repl_pos1, strlen("teacher"), "professor");     }        printf("%s\n", str1);        char *del_pos1 = strstr(str1, "I am ");     if (del_pos1 != NULL)     {         //strrepl(del_pos, strlen("I am "), "");         strmove(del_pos1, del_pos1 + strlen("I am "), 1);     }        printf("%s\n", str1); }    void wide_replace(void) {     wchar_t str2[200] = L"I am a teacher.";        wprintf(L"%s\n", str2);        wcsrepl(str2 + 7, 0, L"good ");        wprintf(L"%s\n", str2);        wchar_t *repl_pos2 = wcsstr(str2, L"teacher");     if (repl_pos2 != NULL)     {         wcsrepl(repl_pos2, wcslen(L"teacher"), L"professor");     }        wprintf(L"%s\n", str2);        wchar_t *del_pos2 = wcsstr(str2, L"I am ");     if (del_pos2 != NULL)     {         //wcsrepl(del_pos2, wcslen(L"I am "), L"");         wcsmove(del_pos2, del_pos2 + wcslen(L"I am "), 1);     }        wprintf(L"%s\n", str2); }                          int wmain(int argc, wchar_t* argv[]) {     _setmode(_fileno(stdin), _O_U16TEXT); // unicode console     _setmode(_fileno(stdout), _O_U16TEXT);     _setmode(_fileno(stderr), _O_U16TEXT);        wprintf(L"wchar_t[]:\n");     wide_replace();     _setmode(_fileno(stdin), _O_TEXT); // non-unicode console     _setmode(_fileno(stdout), _O_TEXT);     _setmode(_fileno(stderr), _O_TEXT);     printf("char[]:\n");     narrow_replace();        return 0; }</wchar.h></string.h></stdio.h></io.h></fcntl.h>

[修改于 10年1个月前 - 2015/10/10 12:44:23]

来自:计算机科学 / 软件综合
1
新版本公告
~~空空如也

想参与大家的讨论?现在就 登录 或者 注册

所属专业
上级专业
同级专业
acmilan
进士 学者 笔友
文章
461
回复
2934
学术分
4
2009/05/30注册,6年8个月前活动
暂无简介
主体类型:个人
所属领域:无
认证方式:邮箱
IP归属地:未同步
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

笔记
{{note.content}}
{{n.user.username}}
{{fromNow(n.toc)}} {{n.status === noteStatus.disabled ? "已屏蔽" : ""}} {{n.status === noteStatus.unknown ? "正在审核" : ""}} {{n.status === noteStatus.deleted ? '已删除' : ''}}
  • 编辑
  • 删除
  • {{n.status === 'disabled' ? "解除屏蔽" : "屏蔽" }}
我也是有底线的