C/C++ isspace() 跳过多字节字符串字符
Posted
技术标签:
【中文标题】C/C++ isspace() 跳过多字节字符串字符【英文标题】:C/C++ isspace() skipping multibyte string characters 【发布时间】:2014-02-13 12:24:03 【问题描述】:我有以下函数返回从字符串中去除空格,
char *rtrim(char *l_ptr)
char *lptr = l_ptr + strlen(l_ptr) - 1;
for (; lptr != l_ptr && isspace((int)*lptr); lptr--)
;
*lptr = '\0';
return lptr;
char *ltrim(char *l_ptr)
char *lptr;
for (lptr = l_ptr; *lptr != '\0' && isspace((int)*lptr); lptr++)
;
return lptr;
char *trim(char *l_ptr)
return rtrim(ltrim(l_ptr));
问题是它的修剪字符如下 -
从“中删除前导空格
Ć"
删除了前导空格,结果是“”
字符是 0xc6,前面有几个空格。我已经检查了包含 setlocale(LC_ALL, ""); 的代码。 LANG 设置为 pl_PL.isoo88592。非常感谢任何帮助。
谢谢。
【问题讨论】:
你使用的是什么字符编码? @JohnZwinck 根据他的 LANG 设置,我猜是 ISO 8859-2(Latin-2,用于东欧)。 注意:如果l_ptr
是""
,则char *lptr = l_ptr + strlen(l_ptr) - 1;
是UB。 (也许也是一个 long 循环。)
如果char
已签名,则字符不是 0xC6,而是-0x3A。相同的 8 位模式,带有 2 的补码,但提升为 int
时的值不同。
【参考方案1】:
问题在于您如何调用isspace
。仅限isspace
如果输入在[0,UCHAR_MAX]
范围内,则已定义结果
(或者是EOF
)。在您的系统上,char
可能已签名,这
表示(int)*lptr
将导致
重音字符(代码点大于 127 的字符),
不在合法范围内。
当调用is...
的一种参数形式时(那些在
<cctype>
或 <ctype.h>
),你应该总是投射任何
char
键入 unsigned char
: isspace( static_cast<unsigned
char>( *lptr ) )
。 (unsigned char
的隐式转换
到int
会做正确的事。)
【讨论】:
虽然根据标准您是完全正确的,但您确定这是导致 OP 问题的原因吗?如果我没记错的话,至少 glibc 有意在isspace
等函数中支持负值作为标准要求的扩展。
@hvd 它可以解释症状。尽管正如您所说,某些实现 确实 会努力使其正常工作(Latin-1 中的 'ÿ'
除外,其代码点为 0xFF);我记得在 Solaris 下就是这种情况。 (我不记得在 Linux 下尝试过,看看 glibc
做了什么。)我还要求过去至少有一次,在 Windows(使用 VC++)下不是这种情况。【参考方案2】:
rtrim()
有多个问题。
isspace()
仅在 unsigned char
和 EOF 范围内为 int
定义。对于 0 到 CHAR_MAX 范围之外的值(通常为 0 到 127),需要在隐式转换为 int
之前转换为 unsigned char
。 (@James Kanze)
C11dr §7.4.1 “...其值应表示为
unsigned char
或应等于宏EOF
的值。如果参数具有任何其他值,则行为未定义。”
char *lptr = l_ptr + strlen("") - 1;
不好,因为该指针值不知道是否有效。需要新方法。这也用
for (; lptr != l_ptr ... ; lptr--)
*lptr = '\0'; return lptr;
总是返回""
。 @hvd 可能需要字符串的开头。
建议重写:
#include "ctype.h"
char *rtrim(char *l_ptr)
unsigned char *ptr = (unsigned char *) l_ptr;
unsigned char *end = ptr;
while (*ptr)
if (!isspace(*ptr++))
end = ptr;
*end = '\0';
return l_ptr;
【讨论】:
@user3305937 如果其中一个答案很好地满足您的帖子的需求,请接受它。在您获得 15 个以上的代表点数后,请务必对您认为有用的所有答案进行投票。【参考方案3】:您的rtrim
函数以
*lptr = '\0';
return lptr;
除了将被视为空字符串之外,这永远不会返回任何内容。然后在trim
中直接返回该结果。
根据您希望这些函数如何工作,您应该让rtrim
返回l_ptr
的原始值,它保持不变并指向字符串的开头,或者让trim
忽略返回rtrim
的值。
所有字符都会有同样的问题,而不仅仅是'Ć'
。
【讨论】:
我认为在rtrim
中返回字符串的结尾没有 有问题,而是rtrim
用于实现trim
的方式。
@hmn 哦,当然,我想这也是一种有效的方法。将编辑。【参考方案4】:
如果您使用的是多字节字符,切换到wchar
可能会更容易,以避免不必要的 char(pointer) 操作麻烦?
您可以使用iswspace
来检查字符是否为空格。
【讨论】:
他的环境似乎说他使用的是 ISO 8859-2,它不是多字节的。以上是关于C/C++ isspace() 跳过多字节字符串字符的主要内容,如果未能解决你的问题,请参考以下文章