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

时间:2011-01-03 22:17:52

标签: c string file character

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

5 个答案:

答案 0 :(得分:8)

  

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

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

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

问题“What is the best unicode library for C?”的答案是使用ICU库。您可能需要查看ustdio.h,因为它具有u_fgetc功能,并且为您的程序添加Unicode支持可能只需输入u_几次就可以了。

另外,如果你可以花几分钟时间阅读一些内容,你可能需要阅读Joel On Software的The Absolute Minimum Every Software Developer Absolutely, Positively Must Know about Unicode and Character Sets (No Excuses!)

我个人从未使用过ICU,但从现在开始我可能会这样做: - )

答案 1 :(得分:4)

如果要在运行时编写一个尊重当前语言设置的wc实用程序的标准C版本,那么您确实可以使用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更改为wint_tgetchar()更改为getwchar()EOF更改为{{} 1}}和WEOFisspace()

iswspace()

答案 2 :(得分:2)

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

答案 3 :(得分:1)

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

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

希望这有帮助。

答案 4 :(得分:0)

您确定您确实需要字符的数量吗? 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- 8验证检查数据。如果通过,则返回UTF-8前导字节数。如果失败,则返回总字节数。

(请注意,上述方法仅适用于wc。如果您实际上正在使用字符进行某些操作而不是仅计算它们,则需要知道编码。 )