在C中读取和输出unicode

时间:2013-03-16 17:03:56

标签: c

FILE * f = fopen("filename", "r");
int c;

while((c = fgetc(f)) != EOF) {
    printf("%c\n", c);
}

您好,我已经搜索了整整一个小时,发现了很多关于Unicode的明智论文,但没有回答这个简单的问题:

这四行的最短等价物,可以在Linux上使用gcc和bash来管理UTF8。

谢谢

1 个答案:

答案 0 :(得分:6)

根据您的系统,这样的事情应该有效:

#include <stdio.h>
#include <wchar.h>
#include <locale.h>


int main() {
   setlocale(LC_CTYPE, "en_GB.UTF-8");
   FILE * f = fopen("filename", "r");
   wint_t c;

   while((c = fgetwc(f)) != WEOF) {
      wprintf(L"%lc\n", c);
   }
}

原始代码的问题在于C没有意识到(或关心)字符是多字节的,因此多字节字符将被每个字节之间的\n破坏。对于此版本,字符被视为UTF-8,因此%lc现在可以表示多达6个实际字节,这些字节可以保证正确输出。如果输入有任何ASCII,它只会像以前一样使用每个字符一个字节(因为ASCII与UTF-8兼容)。

strace对于调试这样的事情总是有用的。例如,如果文件仅包含££(£具有UTF-8序列\ 302 \ 243)。您的版本产生:

write(1, "\302\n\243\n\302\n\243\n\n\n", 10) = 10

我的,

write(1, "\302\243\n\302\243\n", 6)     = 6

请注意,一旦您读取或写入流(包括stdout),它就会设置为字节或宽方向,如果您想要更改它,则需要重新打开流。例如,如果您想要读取UTF-8文件,但将stdout保留为面向字节,则可以将wprintf替换为:

  printf("%lc\n", c);

这涉及后台的额外代码(转换格式),但提供了与期望字节流的其他代码更好的兼容性。