删除C中字符串中的所有非字母字符--可能是编译器问题
我正在用C编写一个函数,它将接受一个字符串并删除所有不是小写字母字符的字符。到目前为止,我已经编写了以下代码:删除C中字符串中的所有非字母字符--可能是编译器问题,c,alphabet,string.h,C,Alphabet,String.h,我正在用C编写一个函数,它将接受一个字符串并删除所有不是小写字母字符的字符。到目前为止,我已经编写了以下代码: void strclean(char* str) { while (*str) { if (!(*str >= 'a' && *str <= 'z')) { strcpy(str, str + 1); str--; } str++; } } 当我让它在输入if语句的每一行之
void strclean(char* str) {
while (*str) {
if (!(*str >= 'a' && *str <= 'z')) {
strcpy(str, str + 1);
str--;
}
str++;
}
}
当我让它在输入if语句的每一行之后打印时,我收到的输出如下:
hello][]woldd
hello[]woldd
hello]woldd
hellowoldd
它似乎真的很接近,但我不明白为什么它会产生这种输出!最奇怪的是,我已经把代码给了另外两个朋友,并且在他们的电脑上运行得很好。我们都运行相同版本的Linux(ubuntu 14.04.3),并且都使用gcc进行编译
我不确定代码是否存在会导致输出不一致的问题,或者是编译器问题造成了问题。与他们的机器相比,这可能与我的机器上的strcpy有关。如果范围重叠,
strcpy
功能不能保证工作,就像您的情况一样。从C117.24.2.3 strcpy功能/2
(我的重点):
strcpy
函数将s2
指向的字符串(包括终止的空字符)复制到s1
指向的数组中如果复制发生在重叠的对象之间,则行为未定义。
根据C11 7.24.2.2 memmove函数/2
,您可以使用类似于memmove
的功能,它可以处理重叠的范围:
memmove
函数将n
字符从s2
指向的对象复制到s1
指向的对象中复制发生时,就像将s2
指向的对象中的n
字符首先复制到一个由n
字符组成的临时数组中,该数组不会与s1
和s2
指向的对象重叠一样,然后将临时数组中的n
字符复制到s1
所指向的对象中
但是有一个更好的解决方案,即时间复杂度
O(n)
而不是O(n2)
,同时仍然是重叠安全的:
void strclean (char* src) {
// Run two pointers in parallel.
char *dst = src;
// Process every source character.
while (*src) {
// Only copy (and update destination pointer) if suitable.
// Update source pointer always.
if (islower(*src)) *dst++ = *src;
src++;
}
// Finalise destination string.
*dst = '\0';
}
您会注意到,我还使用islower()
(来自ctype.h
)来检测小写字母字符。由于C标准不要求字母字符具有连续的代码点(数字是唯一保证连续的数字),因此这更便于移植
也不需要单独检查isalpha()
,因为根据C117.4.1.2 isalpha函数/2
,islower()==true
意味着isalpha()==true
:
isalpha
函数测试isupper
或islower
为真的任何字符,或
从7.21.2.3开始,strcpy功能
如果复制发生在
重叠,则行为未定义
memmove
即使区域重叠也可以使用
void strclean(char* str) {
while (*str) {
if (!islower(*str)) { /* include ctype.h to use islower function */
memmove(str, str + 1, strlen(str)); /* strlen(str + 1) + 1 (for terminating null character) should be strlen(str) */
} else {
str++;
}
}
}
由于指针的减法是未定义的行为,因此它指向数组之前的区域,因此我还重新构造了
str
操作。嗯,哦!strcpystrcpy
中的字符串不能重叠。作为一般提示,不要怀疑编译器或实现(在实践中,它们非常可靠),而是首先怀疑您自己的代码。阅读更多关于str--当str
指向顶部时,代码>使指针无效。哦,是的,这是一个很好的观点。我也没想到。谢谢大家!如果islower
返回“true”,那么isalpha
是否也会返回“true”呢?意思是你不需要检查isalpha
吗?@Joachim,你是正确的,因为isalpha至少适用于islower
或isupper
的所有内容。我会调整答案的。谢谢!这个答案非常简洁,因为它几乎不会改变我的代码。我不知道memmove的事。我认为上面的人对我的代码做了很好的改进,但我喜欢你的答案,因为它详细说明了我的问题。MikeCAT,根据BLUEPIXY对问题的评论,从指向数组开头的指针中减去是未定义的行为。您可以通过删除str--
并将str++
放入else
块,相对简单地消除这种情况。我认为把strcpy留在那里也是一种疏忽。我会做出这些改变,但如果你不高兴,请随时改进。虽然并没有消除O(n^2)的性质,但它仍然是一个有用的答案,除了极长的字符串(它们很少且相隔很远)。
void strclean(char* str) {
while (*str) {
if (!islower(*str)) { /* include ctype.h to use islower function */
memmove(str, str + 1, strlen(str)); /* strlen(str + 1) + 1 (for terminating null character) should be strlen(str) */
} else {
str++;
}
}
}