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...的一种参数形式时(那些在 &lt;cctype&gt;&lt;ctype.h&gt;),你应该总是投射任何 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; 不好,因为该指针值不知道是否有效。需要新方法。这也用

    启动了一个 long 循环

    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() 跳过多字节字符串字符的主要内容,如果未能解决你的问题,请参考以下文章

用c语言写个函数测试处理器是大端还是小端程序怎么写?

Java IO读写的常用方法

isspace()方法

Python3 isspace()方法

Java中有几种类型的流

isspace字符串测试函数应用实例