【问题标题】:Count characters in UTF8 when plain char is unsigned当普通字符无符号时计数 UTF8 中的字符
【发布时间】:2013-01-09 10:06:38
【问题描述】:

在 UTF8 中,我使用这个函数来计算字符数(不是字节数):

int schars(const char *s)
{
    int i = 0;

    while (*s) {
        if ((*s & 0xc0) != 0x80) i++;
        s++;
    }
    return i;
}

这是否适用于纯 charunsigned char 的实现?

【问题讨论】:

  • 有趣,几乎没有关系,事实:普通char unsigned char默认在gcc下。在 MSVC 下,它是 signed char。按照标准,charunsigned charsigned char 是三种不同的类型。
  • @Corbin 我认为普通的 char 在 gcc 上是 signed char
  • 本可以发誓 gcc 默认已签名,但看起来您是对的。哎呀:)。
  • 这是因为默认的char 类型具有实现定义的签名,不像int 等所有其他整数类型,它们都保证默认签名。
  • @Corbin, David:对于 GCC,“如果你不指定 -fsigned-char-funsigned-char 的默认值”取决于目标。当您为新目标配置 GCC 时,如果您不配置任何内容,则可能存在“默认值的默认值”。我不知道。

标签: c unsigned-char


【解决方案1】:

char 未签名时和已签名时一样有效。

在有符号 2 的补码表示和无符号表示中,UTF8 代码单元的第 8 位和第 7 位是 10 当且仅当代码单元不是第一个代码单元的一个代码点。因此,您将每个代码点的第一个代码单元计数为 1。

int 不能保证是一个足够大的类型来包含每个字符串中的字符数,但我假设你不在乎 ;-)

“字符”可能是一个模棱两可的术语。此代码计算 Unicode 代码点,这与可显示字符(“字素”)不同。有时多个代码点代表一个字素,例如当组合标记用于重音时。了解 Unicode 字符串中有多少个代码点的唯一实际用途是计算编码为 UTF-32 时它将占用多少字节。如果你细心,你可以确保唯一需要处理“字符”的代码是字体引擎,加上一些复杂的操作,如 Unicode 规范化和字符编码。

【讨论】:

  • 谢谢史蒂夫,是的,size_t 是一个更好的选择,但我使用 int 来避免每次通话都出现强制转换
【解决方案2】:

应该的。

您只使用二元运算符,无论底层数据类型是有符号还是无符号,它们的功能都相同。唯一的例外可能是 != 运算符,但您可以用 & 替换它,然后用 ! 包含整个内容,ala:

!((*s & 0xc0) & 0x80)

然后你就只有二元运算符了。

您可以通过检查ANSI C Standard 的第 3.3.10 节来验证字符是否被提升为整数,该节指出“[按位与] 的每个操作数都应具有整数类型。”

编辑

我修改我的答案。根据 ANSI C 标准 3.3,有符号和无符号的位运算不同:

一些运算符(一元运算符 ~ 和二元运算符 > 、 & 、 ^ 和 | , 统称为按位运算符)应具有整数类型的操作数。 这些运算符返回 取决于整数内部表示的值,以及 因此对于签名类型具有实现定义的方面

事实上,对有符号整数执行按位运算被列为可能的安全漏洞here

在 Visual Studio 编译器中,有符号和无符号的处理方式相同(请参阅 here)。

正如this SO question 所讨论的,最好使用unsigned char 对内存进行逐字节读取和内存操作。

【讨论】:

  • 谢谢理查德,我就是这么想的
  • @DavidRF,我一直在思考这个问题,现在我不太确定我的答案。如果您正在阅读unsigned char,一切都会好起来的。实际上,我不确定当您在 signed char 上执行 & 时会发生什么。我正在尝试弄清楚如何进行安全转换。
  • 是的,“Ramón”使用 !((*s & 0xc0) & 0x80) 返回 4 而不是 5,感谢您的建议
  • @DavidRF,我尝试改写问题here - 希望那里的一些答案可以解决这个问题。
【解决方案3】:

是的,会的。

*s 将在计算发生之前提升为 int。因此,您的代码相当于:

if (((int) *s & 0xC0) != 0x80) i++;

即使char 未签名,上述内容也将起作用。

【讨论】:

  • 严格来说,文字 0xC0 和 0x80 已经是 int 类型,所以唯一发生的提升是 *s 被转换为 int 的那个。
  • @Lundin,你是对的,没有 char 文字。答案已相应更新,谢谢:)
  • (在 C++ 中,它们是 char 类型)
  • @Lundin: 在 C++ 中,0xC00x80 具有与 C 相同的类型 int。C++ 中的区别在于 'a' 具有类型 char,而在 C 中它类型为int
  • 是的,'\xC0' 将是 char,但普通的 0xC0int
猜你喜欢
  • 2013-12-07
  • 2015-07-25
  • 1970-01-01
  • 2021-10-17
  • 2010-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多