【问题标题】:How do I compare single multibyte character constants cross-platform in C?如何在 C 中跨平台比较单个多字节字符常量?
【发布时间】:2022-01-20 17:57:30
【问题描述】:

my previous post 中,我找到了使用C++ 字符串执行此操作的解决方案,但我想知道在C 中是否也有使用char 的解决方案。

我目前的解决方案使用str.compare()size() 的字符串,如my previous post 所示。

现在,由于我在std::string 中只使用一个(多字节)字符,是否可以使用char 来实现相同的效果?

例如if( str[i] == '¶' )?如何使用char's 实现这一目标?

(编辑:如 cmets 中所指出的,在 SO 上为比较运算符创建了一个类型)

【问题讨论】:

  • 使用strcmp()比较C中的字符串。
  • if( str[i] = '¶' ) 你分配了一个值,你没有做测试
  • 术语 multibytechar 会暗示(至少对我而言)您的愿望存在基本的不兼容。 char(根据定义)大小为一个字节。
  • 我对“如何使用 char 实现这一点?”感到困惑。 str[i] 和 '¶' 都是char 类型。也许您希望分配char foo = str[i],然后分配if (foo == '¶')。这假设您打算比较而不是分配,如上所述
  • @MANA624:实际上,在 C 中(但不是在 C++ 中),单引号文字的类型为 int,而不是 char

标签: c character-encoding cross-platform multibyte


【解决方案1】:

如何在 C 中跨平台比较单个多字节字符常量?

您的意思似乎是使用单个多字节字符表示的 整数字符常量。那么,首先要认识到的是,在 C 中,整数字符常量(例如:'c''¶')的类型为 int,而不是 char。 C17 的主要相关部分是第 6.4.4.4/10 段:

整数字符常量的类型为int。包含映射到单字节执行字符的单个字符的整数字符常量的值是被解释为整数的映射字符表示的数值。 包含多个字符(例如,'ab')的整数字符常量的值,或包含不映射到单字节执行字符的字符或转义序列的值是实现定义的。 如果整数字符常量包含单个字符或转义序列,则其值是将类型为char(其值为单个字符或转义序列的值)的对象转换为类型int 时产生的值。

(已添加重点。)

请注意,“定义的实现”意味着从一开始就限制了可移植性。即使我们排除了定义不正当行为的实现,我们仍然有替代方案,例如

  • 实现拒绝包含多字节源字符的整数字符常量;或
  • 实现拒绝不映射到单字节执行字符的整数字符常量;或
  • 无论字节序列在执行字符集中的重要性如何,实现都会通过按字节标识映射来映射源多字节字符。

这不是一个详尽的清单。

您当然可以相互比较整数字符常量,但如果它们映射到多字节执行字符,那么您无法将它们与单个 chars 进行比较。

由于您的预期应用程序似乎是在 C 字符串中定位单个多字节字符,最自然的做法似乎是使用标准的 strstr() 函数实现您的 C++ 方法的 C 模拟。示例:

    char str[] = "Some string ¶ some text ¶ to see";
    char char_to_compare[] = "¶";
    int char_size = sizeof(char_to_compare) - 1;  // don't count the string terminator

    for (char *location = strstr(str, char_to_compare);
            location;
            location = strstr(location + char_size, char_to_compare)) {
        puts("Found!");
    }

这在很多情况下都是正确的,但仍然对于某些执行字符编码中的某些字符可能是错误的,例如具有多个移位状态的编码。

如果您希望对基本执行字符集之外的字符进行稳健处理,那么建议您控制内存中的编码,并对该编码执行适当的转换、操作和转换。例如,这在很大程度上是 ICU 所做的。

【讨论】:

  • 哇!谢谢你这么好的回答。这给了我更多的东西来了解情况并为我指明正确的方向
【解决方案2】:

我相信你的意思是这样的:

char a = '¶';
char b = '¶';

if (a == b) /*do something*/;

上面的可能行也可能行不通,如果'¶'的值大于char范围,那么就会溢出,导致ab存储与 '¶' 不同的值。不管它们持有哪个值,它们实际上可能都具有相同的值。

请记住,char 类型只是一个单字节宽(8 位)整数,因此为了使用多字节字符并避免溢出,您只需使用更宽的整数类型 (short, int,长...)

short a = '¶';
short b = '¶';

if (a == b) /*do something*/;

根据个人经验,我还注意到,有时您的环境可能会尝试使用与您需要的字符编码不同的字符编码。例如,尝试打印 'á' 字符实际上会产生其他内容。

unsigned char x = 'á';
putchar(x); //actually prints character 'ß' in console.
putchar(160); //will print 'á'.

发生这种情况是因为控制台使用了扩展 ASCII 编码,而我的编码环境实际上使用 Unicode,为 'á' 解析值 225 而不是我想要的值 160。

【讨论】:

  • 感谢您的回答。在我看来,这似乎是一个适当的解决方案。唯一的问题是,为了获得第一个short,我需要从std::string 中获取多字节值,而使用str[ i ] 可能无法实现,因为它返回char
  • @Z0q 我对 C++ 字符串不是很熟悉,但对于宽字符字符串,我认为您应该改用 std::wstring。这样,当您访问元素 str[i] 时,您将访问 wchar_t(无符号短)而不是 char
  • 谢谢@Raxus,问题是我需要跨平台支持,wchar_t 的大小在 Windows 和 Linux 上是不同的
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-01
  • 1970-01-01
相关资源
最近更新 更多