繁体   English   中英

从 C 中的文件中读取非 ascii 字符

[英]Reading non-ascii characters from a file in C

我有一个文件foo.txt ,它只是:

” ’

char x = fgetc(myfile);

当我在文件上使用fgetc时,我在两个字符上都得到了一个226的常量值。 为什么是这样? 我怎样才能解决这个问题?

这是我的代码:

FILE* f = fopen("./debate.txt", "rb");
int x = fgetc(f);
char y = (char)x;

对于普通(便携式)软件,字符编码是一个痛苦的世界。 问题(和潜在的解决方案)是:

A) 文本文件可以采用任何随机/“文本编辑器定义”编码。

为了解决这个问题,有 4 个选项:

  • 期望以特定编码(例如 UTF-8)输入并拒绝支持其他任何内容(如果文件中的数据对于您选择的编码无效,则会生成错误消息)。 这会惹恼一些用户(例如,国家标准与 CNS 11643 等不兼容的地方)。

  • 支持多种编码,并让用户选择期望的编码(例如基于命令行参数)。 这对用户来说有点不方便,对你来说非常痛苦。

  • 支持多种编码,并尝试自动检测文件使用的编码。 这对用户来说更方便一点,直到它猜错并成为一个主要的烦恼(并且你不能将猜错编码的机会减少到零)。

  • 支持多种编码,让用户根据需要选择编码,如果用户没有指定,则自动检测。 这对用户来说是最好的选择(对软件开发人员来说也是最糟糕的选择)。

对于这些选项,我会使用第一个(我会说“输入文件必须是 UTF-8”,部分原因是 UTF-8 已经变得非常普遍并且得到很好的支持,部分原因是由于技术原因,其他所有编码都可能更糟)。 请注意(根据您的结果),您的输入文件极有可能位于 UTF-8 中。

B) 无论编译器对char使用什么,都是实现定义的(可以是 ASCII,可以是 EBDIC,也可以是其他任何东西),并且可以是有符号的或无符号的。

在这种情况下,假设 ASCII 是“非常安全的”(对于可移植性)。 假设 UTF-8 是第二个最佳选择,但它会在“可能有符号” char值上执行任何数学运算(例如右移等)的任何代码产生问题。

C) stdinstdoutstderr管道也是随机/实现定义的。

这与前一个问题类似,除了最佳解决方案(“假设 ASCII”)要困难得多(尤其是当您想要 output 错误消息等包含输入文件中的文本时)。 为此,我很想尽可能多地使用 ASCII,但如果必须的话,我会欺骗 output UTF-8。 如果操作系统(或外壳)无法处理 UTF-8 它将造成混乱,但大多数用户会理解(并且可以通过将 output 传输到文件来解决它)。 最好的选择(用于用户输出)是使用 GUI 而不是使用stdout ,但这会产生大量额外问题(并导致第二大额外问题 - 诸如错误消息等的国际化)。

D)无论编译器对wchar的假设是随机/实现定义的(也许是 UTF-16,也许是 UTF-32,也许是其他任何东西;它甚至可能是一个根本不是“宽”的 8 位编码)。

这里唯一明智的选择是认识到wchar是一个不可用的故障,不应该(在任何情况下)用于任何事情。

更具体的说, wchar是基于以往历史错误的历史错误。 从本质上讲,在早期,微软和 Sun 决定采用 UCS-2(“所有 Unicode 代码点都适合 16 位”假设)很快就被打破了。 为了解决这个问题,Microsoft 和 Sun 转而使用 UTF-16,但 Microsoft 主要在 little-endian 机器上运行并选择了 UTF-16LE,而 Sun (Java) 的目标是 big-endian 机器并选择了 UTF-16BE。 wchar扩展于 1995 年被添加到 C 中,同时公司(Microsoft、Sun)做错了所有事情并且没有做任何相互兼容的事情; 所以wchar最终变成了一个“我们不知道标准是什么,所以我们的标准根本不是标准”的笑话。 对于 C(和 C++),这在 2011 年通过在<uchar.h>中引入char16_t (UTF-16) 和char32_t (UTF-32) 得到修复,但采用速度很慢(例如微软仍然懒得理会 C99 )。

请注意,问题的另一部分是人们希望假设一个wchar是一个完整的可打印字符,而这几乎从来不是这种情况(例如,即使对于 UTF-32,其中一个wchar是一个完整的 Unicode 代码点也有组合代码点) ; 这破坏了任何“宽字符”实现的任何好处(即使您的代码根本不可移植并且您知道wchar实际上是什么)。

最好的解决方案(特别是如果您选择“期望输入文件使用 UTF-8”来解决第一个问题)是使用存储在uint8_t中的 UTF-8 (这样没有人会混淆它的任何char )。

在这种情况下; “将文件中的输入转换为您的内部字符编码”可以变成“什么都不做,将 UTF-8 转换为 UTF-8”; 并且“将您的内部字符编码转换为stdout想要的任何内容”变为“通过几乎什么都不做(从uint8_t转换为char )将 UTF-8 转换为 ASCII(或 UTF-8)”。 换句话说,它可以非常接近“对所有事物使用相同的编码”。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM