如果您的系统支持,类似下面这样的内容应该可以正常工作:
#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
And mine,
write(1, "\302\243\n\302\243\n", 6) = 6
请注意,一旦您读取或写入流(包括
stdout
),它将设置为字节或宽字符方向,并且如果您想更改它,则需要重新打开流。因此,例如,如果您想读取UTF-8文件,但保留
stdout
作为字节方向,请用以下代码替换
wprintf
:
printf("%lc\n", c);
这需要在后台添加额外的代码(用于转换格式),但可以提供更好的兼容性,以满足其他期望字节流的代码。