在C中读取和输出unicode

pou*_*ler 4 c

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

while((c = fgetc(f)) != EOF) {
    printf("%c\n", c);
}
Run Code Online (Sandbox Code Playgroud)

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

什么是这四行的最短等价物,可以在Linux上使用gcc和bash管理UTF8.

谢谢

tep*_*pic 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);
   }
}
Run Code Online (Sandbox Code Playgroud)

您的原始代码的问题是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
Run Code Online (Sandbox Code Playgroud)

我的,

write(1, "\302\243\n\302\243\n", 6)     = 6
Run Code Online (Sandbox Code Playgroud)

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

  printf("%lc\n", c);
Run Code Online (Sandbox Code Playgroud)

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