在C中处理多字节(非ASCII)字符

我正在尝试自己的版本的wc(unixfilter),但我有非ASCII字符的问题。 我做了一个文本文件的HEX转储,发现这些字符占用多个字节,所以它们不适合char。 有什么方法我怎么能从文件中读取这些字符并像C一样处理它们(所以我可以计算文件中的字符)? 我一直在谷歌搜索并找到一些wchar_t类型,但没有任何简单的例子如何将它与文件一起使用。

我一直在谷歌搜索并找到一些wchar_t类型,但没有任何简单的例子如何将它与文件一起使用。

很满意。 没有任何简单的例子,因为不幸的是,正确的字符集支持并不简单

旁白:在一个理想的世界中,每个人都会使用UTF-8(一种具有内存效率,健壮且向后兼容ASCII的Unicode编码),标准C库将包括UTF-8编码解码支持,以及答案对于这个问题(以及一般的文本处理)将是简单和直接的。

问题“ 什么是C的最佳unicode库? ”的答案是使用ICU库。 您可能希望查看ustdio.h ,因为它具有u_fgetc函数,并且为您的程序添加Unicode支持可能只需输入几次u_fgetc

此外,如果您可以节省几分钟的时间阅读,您可能需要阅读绝对最低限度的每个软件开发人员绝对必须知道的 Joel On Software的Unicode和字符集(No Excuses!) 。

我个人从来没有使用过ICU,但我可能会从现在开始:-)

如果要在运行时编写符合当前语言设置的标准C版本的wc实用程序,那么您确实可以使用stdio函数的wchar_t版本。 在程序启动时,您应该调用setlocale()

 setlocale(LC_CTYPE, ""); 

这将导致宽字符函数使用由环境定义的适当字符集 – 例如。 在类Unix系统上, LANG环境变量。 例如,这意味着如果您的LANG变量设置为UTF8语言环境,则宽字符函数将处理UTF8中的输入和输出。 (这是指定POSIX wc实用程序的工作方式)。

然后,您可以使用所有标准函数的宽字符版本。 例如,如果你有这样的代码:

 long words = 0; int in_word = 0; int c; while ((c = getchar()) != EOF) { if (isspace(c)) { if (in_word) { in_word = 0; words++; } } else { in_word = 1; } } 

…你可以将c转换为宽字符版本,方法是将c更改为wint_t ,将getchar()更改为getwchar() ,将EOF更改为getwchar() ,将isspace()更改为iswspace()

 long words = 0; int in_word = 0; wint_t c; while ((c = getwchar()) != WEOF) { if (iswspace(c)) { if (in_word) { in_word = 0; words++; } } else { in_word = 1; } } 

去看看ICU吧 。 该库是您处理所有问题所需要的。

到目前为止,大多数答案都有其优点,但您使用的答案取决于您想要的语义:

  • 如果要处理已配置的语言环境编码中的文本,并且在遇到无效序列时不关心完全失败,则使用getwchar()就可以了。
  • 如果要以配置的语言环境编码处理文本,但需要检测无效序列并从中恢复,则需要手动读取字节并使用mbrtowc
  • 如果您总是希望将文本处理为UTF-8,则需要读取字节并将其提供给您自己的解码器。 如果您事先知道该文件将是有效的UTF-8,您可以只计算00-7FC2-F4范围内的字节并跳过计算所有其他字节,但是如果存在无效序列,这可能会给出错误的结果。 更强大的方法是将字节流解码为Unicode代码点并计算成功解码的数量。

希望这可以帮助。

你确定你真的需要多少个字符wc计算字节数。

 ~$ echo 'דניאל' > hebrew.txt ~$ wc hebrew.txt 1 1 11 hebrew.txt 

(11 = 5个双字节字符+ 1个字节用于’\ n’)

但是,如果你真的想要计算字符数而不是字节数,并且可以假设你的文本文件是用UTF-8编码的,那么最简单的方法是计算所有不是字节字节的字节(即,在0x80范围内)为0xBF)。

如果您不能假设UTF-8但可以假设任何非UTF-8文件采用单字节编码,则对数据执行UTF-8validation检查。 如果通过,则返回UTF-8前导字节数。 如果失败,则返回总字节数。

(请注意,上述方法特定于wc 。如果你实际上是对字符进行操作而不是仅计算它们,那么你需要知道编码。)