无法使用wchar.h比较UTF-8字符

Ete*_*ght 4 c utf-8 widechar

我正在制作一个小程序来读取一个包含UTF-8元素的文件,char由char填充.读取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);

     }
   } 
Run Code Online (Sandbox Code Playgroud)

我省略了与文件名生成有关的代码,因为它没有提供给案例.这只是字符串操作.

如果我为这个程序提供一个文件,其中包含???? ??? ?????.我希望它返回的单词: ???_ ??? ????_.

搜索互联网没有多大帮助,因为大多数结果非常笼统或谈论有关UTF-8的完全不同的事情.这就像没有人因某种原因需要操纵单个字符.

任何指向我正确方向的东西都是最受欢迎的.我不一定在寻找我提交的代码的直接修复版本,我将非常感谢任何有见识的评论,帮助我理解wchar机制的确切运作方式.整个wbyte,wchar,L,no-L,对我来说是个烂摊子.

预先感谢您的帮助.

nin*_*alj 6

C有两种不同的字符:多字节字符宽字符.

多字节字符可以占用不同的字节数.例如,在UTF-8(Unicode 的可变长度编码)中,a需要1个字节,而?需要2个字节.

宽字符总是占用相同的字节数.此外,a wchar_t必须能够保存执行字符集中的任何单个字符.因此,使用UTF-32,当两个a?采取每4个字节.不幸的是,有些平台的wchar_t宽度为16位:这些平台无法正确支持BMP之外的字符wchar_t.如果__STDC_ISO_10646__已定义,则wchar_t保留Unicode代码点,因此必须(至少)4个字节长(从技术上讲,它必须至少为21位长).

因此,当使用UTF-8时,您应该使用多字节字符,这些字符存储在普通char变量中(但要注意strlen(),这会计算字节数,而不是多字节字符数).

不幸的是,Unicode还有更多.

? 可以表示为单个Unicode代码点,也可以表示为两个单独的代码点:

  • U+03AC GREEK SMALL LETTER ALPHA WITH TONOS←1个代码点←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).


Ben*_*tto 4

首先,请花时间阅读这篇精彩的文章,其中解释了 UTF8 与 Unicode 以及有关字符串和编码的许多其他重要内容:http://www.joelonsoftware.com/articles/Unicode.html

\n\n

您在代码中尝试执行的操作是以unicode逐个字符读取的,并与这些字符进行比较。如果输入流是 UTF8,那么这将不起作用,并且实际上不可能使用这种结构。

\n\n

简而言之:完全 unicode 字符串可以通过多种方式进行编码。其中之一是使用一系列大小相等的“宽”字符,每个字符一个。这就是类型wchar_t(有时是 WCHAR)的用途。另一种方法是 UTF8,它使用可变数量的原始字节来对每个字符进行编码,具体取决于字符的值。

\n\n

UTF8只是一种字节流,可以编码unicode字符串,常用于文件中。它与 WCHAR 字符串不同,后者是更常见的内存中表示形式。您无法可靠地浏览 UTF8 流并直接在其中进行字符替换。您需要读取整个内容并对其进行解码,然后循环遍历结果的 WCHAR 来进行比较和替换,然后将该结果映射回 UTF8 以写入输出文件。

\n\n

在Win32上,使用MultiByteToWideChar进行解码,可以使用相应的WideCharToMultiByte返回。

\n\n

当您使用"string literal"带有常规引号的 a 时,您将创建一个以 null 结尾的 ASCII 字符串 ( char*),该字符串不支持 Unicode。带有L"string literal"前缀的L将创建一个以 null 结尾的 WCHAR 字符串 (wchar_t *),您可以在字符串或字符比较中使用它。L 前缀也适用于单引号字符文字,如下所示:L\'\xce\xb5\'

\n\n
\n\n

正如评论者指出的那样,当您使用 fread/fwrite 时,您应该使用sizeof(wchar_t)而不是其指针类型,因为您尝试读/写的数量是实际的 wchar,而不是指向 1 的指针的大小。这个建议只是独立于上述内容的代码反馈——无论如何你都不想逐个字符地读取输入。

\n\n

还要注意,当您进行字符串比较 ( wcscmp) 时,您应该使用实际的宽字符串(以 nul 宽字符结尾)——而不是使用内存中的单个字符作为输入。如果(当)您想要进行字符与字符的比较时,您甚至不需要使用字符串函数。由于 WCHAR 只是一个值,因此您可以直接比较:if (sample == L\'\xce\xac\') {}

\n