【问题标题】:Handling multibyte (non-ASCII) characters in C在 C 中处理多字节(非 ASCII)字符
【发布时间】:2011-06-03 02:23:03
【问题描述】:

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

【问题讨论】:

  • 您将需要了解 Unicode,尤其是编码。您目前知道这些术语的含义吗?
  • 另外,您可能想了解非 ASCII 单字节编码,例如各种 ISO 编码、Windows 1252 等。您可能知道,ASCII 实际上是 7 位宽的编码。跨度>
  • @Joey 谢谢,所以习惯了 ASCII、ISO、ANSI 等。我养成了一个坏习惯!

标签: c string file character


【解决方案1】:

我在谷歌上搜索了一下,发现了一些 wchar_t 类型,但没有任何简单的示例如何将它与文件一起使用。

很好认识。没有任何简单的示例,因为不幸的是,正确的字符集支持并不简单

除此之外:在理想的世界中,每个人都会使用 UTF-8(一种 Unicode 编码,内存效率高、健壮且与 ASCII 向后兼容),标准 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,但从现在开始我可能会使用 :-)

【讨论】:

  • 我真的很喜欢你的:“每个软件开发人员绝对、绝对必须了解 Unicode 和字符集的绝对最低要求(没有借口!)”。很棒的帖子!。
【解决方案2】:

如果您想编写一个标准 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 更改为wint_tgetchar() 更改为getwchar()EOF 更改为WEOFisspace() 更改为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;
    }
}

【讨论】:

  • 在没有解释性评论的情况下投反对票是不礼貌的。
【解决方案3】:

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

【讨论】:

    【解决方案4】:

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

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

    希望这会有所帮助。

    【讨论】:

      【解决方案5】:

      你确定你真的需要字符数吗? 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。如果您实际上是在对字符进行操作而不是仅仅计算它们,那么您需要知道编码。)

      【讨论】:

      • 请注意,wc -m 确实计算 characters 而不是 bytes - wc -m hebrew.txt 给出输出 6 hebrew.txt
      猜你喜欢
      • 2013-07-13
      • 1970-01-01
      • 2011-07-11
      • 1970-01-01
      • 2013-03-22
      • 1970-01-01
      • 2010-12-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多