为了节省内存空间,避免复制来复制去,我们一般喜欢在原地对字符串进行操作。其中最常见的无非是插入、替换、删除。这三个操作有个特点,都可能使字符串长度发生变化。为了适应这种变化,我们在进行这些操作之前,要调整字符串长度。原地操作字符串,最需要注意的一点,是
缓冲区长度一定要足够,比如:
// 这样分配的字符串可以增长到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字符:
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函数,它可以将某个子串替换为另一个子串:
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[]:
// 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=要插入的字符串:
// 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:
// 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:
// 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可以切换回普通模式。
完整程序:
#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>