我正在制作一个小程序,它读取一个包含UTF-8元素的文件,char by char。读完一个字符后,它会将它与其他几个字符进行比较,如果匹配,则用一个下划线'_'替换文件中的字符。
(好吧,它实际上复制了该文件,并用下划线替换了特定的字母。)
我不确定我到底在哪里弄乱,但最有可能到处都是。
这是我的代码:
FILE *fpi;
FILE *fpo;
char ifilename[FILENAME_MAX];
char ofilename[FILENAME_MAX];
wint_t sample;
fpi = fopen(ifilename, "rb");
fpo = fopen(ofilename, "wb");
while (!feof(fpi)) {
fread(&sample, sizeof(wchar_t*), 1, fpi);
if ((wcscmp(L"ά", &sample) == 0) || (wcscmp(L"ε", &sample) == 0) ) {
fwrite(L"_", sizeof(wchar_t*), 1, fpo);
} else {
fwrite(&sample, sizeof(wchar_t*), 1, fpo);
}
}
我省略了与文件名生成有关的代码,因为它没有提供给案例。这只是字符串操作。
如果我向该程序提供包含单词γειά σου κόσμε.
的文件,我希望它返回:
γει_ σου κόσμ_.
搜索互联网并没有多大帮助,因为大多数结果非常笼统或谈论有关UTF-8的完全不同的事情。这就像没有人因某种原因需要操纵单个字符。
任何指向我正确方向的东西都是最受欢迎的。 我不一定在寻找我提交的代码的直接修复版本,我将非常感谢任何有见识的评论,帮助我理解wchar机制的确切运作方式。整个wbyte,wchar,L,no-L,对我来说是一团糟。
提前感谢您的帮助。
答案 0 :(得分:6)
C有两种不同的字符:多字节字符和宽字符。
多字节字符可以使用不同数量的字节。例如,在UTF-8(Unicode的可变长度编码)中,a
占用1个字节,而α
占用2个字节。
宽字符总是占用相同的字节数。此外,wchar_t
必须能够保存执行字符集中的任何单个字符。因此,当使用UTF-32时,a
和α
各占4个字节。不幸的是,有些平台的wchar_t
为16位宽:这些平台无法使用wchar_t
正确支持BMP之外的字符。如果定义了__STDC_ISO_10646__
,则wchar_t
保存Unicode代码点,因此必须(至少)4个字节长(从技术上讲,它必须至少为21位长)。
因此,在使用UTF-8时,您应该使用多字节字符,这些字符存储在普通char
变量中(但要注意strlen()
,这会计算 bytes ,而不是多字节字符)。
不幸的是,Unicode还有更多。
ά
可以表示为单个Unicode代码点,也可以表示为两个单独的代码点:
U+03AC GREEK SMALL LETTER ALPHA WITH TONOS
←1 codepoint←1多字节字符←2字节(0xCE 0xAC
)= 2 char
的。U+03B1 GREEK SMALL LETTER ALPHA
U+0301 COMBINING ACUTE ACCENT
←2个代码点←2个多字节字符←4个字节(0xCE 0xB1 0xCC 0x81
)= 4 char
的。U+1F71 GREEK SMALL LETTER ALPHA WITH OXIA
←1个代码点←1个多字节字符←3个字节(0xE1 0xBD 0xB1
)= 3个char
的。以上所有都是规范的等价物,这意味着它们应该被视为对所有目的都是相同的。因此,您应该使用Unicode规范化算法(有4种:NFC,NFD,NFKC,NFKD)对输入/输出上的字符串进行规范化。
答案 1 :(得分:3)
首先,请花点时间阅读这篇很棒的文章,它解释了UTF8与Unicode以及许多关于字符串和编码的重要事项:http://www.joelonsoftware.com/articles/Unicode.html
您在代码中尝试执行的操作是逐个字符地读取 unicode ,并与之进行比较。如果输入流是UTF8,那将无法工作,并且实际上不可能使用这种结构。
简而言之:完全unicode字符串可以用多种方式编码。其中一个是使用一系列同样大小的“宽”字符,每个字符一个。这就是wchar_t
类型(有时是WCHAR)的用途。另一种方法是UTF8,它使用变量个原始字节来编码每个字符,具体取决于字符的值。
UTF8只是一个字节流,可以编码unicode字符串,通常用在文件中。它与一串WCHAR不同,后者是更常见的内存中表示。您无法可靠地戳穿UTF8流,并直接在其中进行字符替换。您需要读取整个内容并对其进行解码,然后循环遍历WCHAR,以便进行比较和替换,然后将结果映射回UTF8以写入输出文件。
在Win32上,使用MultiByteToWideChar进行解码,您可以使用相应的WideCharToMultiByte返回。
当您使用带有常规引号的"string literal"
时,您将创建一个以空字符结尾的ASCII字符串(char*
),它不支持Unicode。带有L"string literal"
前缀的L
将创建一个以空字符结尾的WCHAR字符串(wchar_t *),您可以在字符串或字符比较中使用它。 L前缀也适用于单引号字符文字,如下所示:L'ε'
正如评论者所指出的,当你使用fread / fwrite时,你应该使用sizeof(wchar_t)
而不是它的指针类型,因为你试图读/写的数量是实际的wchar,而不是a的大小。指向一个。这个建议只是独立于上面的代码反馈 - 你不想一直按字符读取输入字符。
另请注意,当您进行字符串比较(wcscmp
)时,您应该使用实际的宽字符串(以nul宽字符结尾) - 不要在内存中使用单个字符作为输入。如果(何时)你想进行字符到字符的比较,你甚至不需要使用字符串函数。由于WCHAR只是一个值,因此您可以直接进行比较:if (sample == L'ά') {}
。