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

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

为方便起见,定义strmove和wcsmove函数,其中最后一个参数tz=1则复制尾部null字符,tz=0则不复制尾部null字符:
<code class="lang-cpp">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);
}</code>

接着我们可以写出strrepl和wcsrepl函数,它可以将某个子串替换为另一个子串:
<code class="lang-cpp">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);
}</code>

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

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

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

替换子串的话,先用strstr和wcsstr找到要替换的地址,再用strlen和wcslen计算替换前长度,最后调用strrepl和wcsrepl:
<code class="lang-cpp">// 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");
}</code>
替换子串后的字符串『I am a good professor.』。

删除子串和替换子串类似,把src改成空字符串就可以了,然而更高效的方法是直接使用strmove或wcsmove:
<code class="lang-cpp">// 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);
}</code>
删除子串后的字符串『a good professor.』。

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

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

完整程序:
<code class="lang-cpp">#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></code>

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

来自:计算机科学 / 软件综合
1
已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也

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

所属专业
上级专业
同级专业
acmilan
进士 学者 笔友
文章
461
回复
2934
学术分
4
2009/05/30注册,5年3个月前活动
暂无简介
主体类型:个人
所属领域:无
认证方式:邮箱
IP归属地:未同步
文件下载
加载中...
{{errorInfo}}
{{downloadWarning}}
你在 {{downloadTime}} 下载过当前文件。
文件名称:{{resource.defaultFile.name}}
下载次数:{{resource.hits}}
上传用户:{{uploader.username}}
所需积分:{{costScores}},{{holdScores}}下载当前附件免费{{description}}
积分不足,去充值
文件已丢失

当前账号的附件下载数量限制如下:
时段 个数
{{f.startingTime}}点 - {{f.endTime}}点 {{f.fileCount}}
视频暂不能访问,请登录试试
仅供内部学术交流或培训使用,请先保存到本地。本内容不代表科创观点,未经原作者同意,请勿转载。
音频暂不能访问,请登录试试
支持的图片格式:jpg, jpeg, png
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
收藏
取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}