文件io-wchar_t变量只存储C中一半的乌尔都语字符



我正在尝试从文件中读取和操作乌尔都语文本。然而,似乎一个字符并没有被完整地读入wchar_t变量中。这是我的代码,它读取文本并将每个字符打印到一行:

#include <stdio.h>
#include <wchar.h>
#include <locale.h>
void main(int argc, char* argv[]) {
    setlocale(LC_ALL, "");
    printf("This program tests Urdu reading:n");
    wchar_t c;
    FILE *f = fopen("urdu.txt", "r");
    while ((c = fgetwc(f)) != WEOF) {
        wprintf(L"%lcn", c);
    }
    fclose(f);
}

这是我的示例文本:

میرا نام ابراھیم ھے۔
میں وینڈربلٹ یونیورسٹی میں پڑھتا ھوں۔

然而,打印的字符似乎是文本中字母的两倍。我知道宽字符或多字节字符使用多个字节,但我认为wchar_t类型会将字母表中一个字母对应的所有字节存储在一起。

如何读取文本,以便在任何时候都将整个字符存储在变量中?

有关我的环境的详细信息:
gcc:(x86_64-posix-seh-rev0,由MinGW-W64项目构建)5.3.0
操作系统:Windows 10 64位
文本文件编码:UTF-8

这就是我的文本在十六进制格式中的样子:

d9 85 db 8c d8 b1 d8 a7 20 d9 86 d8 a7 d9 85 20 d8 a7 d8 a8 d8 b1 d8 a7 da be db 8c d9 85 20 da be db 92 db 94 ad 98 5d b8 cd ab a2 0d 98 8d b8 cd 98 6d a8 8d 8b 1d 8a 8d 98 4d 9b 92 0d b8 cd 98 8d 98 6d b8 cd 98 8d 8b 1d 8b 3d 9b 9d b8 c2 0d 98 5d b8 cd ab a2 0d 9b ed a9 1d ab ed 8a ad 8a 72 0d ab ed 98 8d ab ad b9 4a

Windows对Unicode的支持大多是专有的,不可能编写使用UTF-8并在Windows上使用Windows本机库的可移植软件。如果你愿意考虑非便携解决方案,这里有一个:

#include <stdio.h>
#include <wchar.h>
#include <locale.h>
#include <fcntl.h>
void main(int argc, char* argv[]) {
    setlocale(LC_ALL, "");
    // Next line is needed to output wchar_t data to the console. Note that 
    // Urdu characters are not supported by standard console fonts. You may
    // have to install appropriate fonts to see Urdu on the console.
    // Failing that, redirecting to a file and opening with a text editor
    // should show Urdu characters.
    _setmode(_fileno(stdout), _O_U16TEXT);
    // Mixing wide-character and narrow-character output to stdout is not
    // a good idea. Using wprintf throughout. (Not Windows-specific)
    wprintf(L"This program tests UTF-8 reading:n");
    // WEOF is not guaranteed to fit into wchar_t. It is necessary
    // to use wint_t to keep a result of fgetwc, or to print with
    // %lc. (Not Windows-specific)
    wint_t c;
    // Next line has a non-standard parameter passed to fopen, ccs=...
    // This is a Windows way to support different file encodings.
    // There are no UTF-8 locales in Windows. 
    FILE *f = fopen("urdu.txt", "r,ccs=UTF-8");
    while ((c = fgetwc(f)) != WEOF) {
        wprintf(L"%lc", c);
    }
    fclose(f);
}

使用glibc的OTOH(例如使用cygwin)不需要这些Windows扩展,因为glibc在内部处理这些事情。

UTF-8是Unicode编码,每个字符占用1-4个字节。我能够将每个unicode字符存储在uint32_t(或某些UNIX平台上的u_int32_t)变量中。我使用的库是(utf8.h|utf8.c)。它为UTF-8字符串提供了一些转换和操作功能。

因此,如果一个文件是UTF-8中的n字节,那么它最多会有nUnicode字符。这意味着我需要4*n字节的内存(每个u_int32_t变量4个字节)来存储文件的内容。

#include "utf8.h"
// here read contents of file into a char* => buff
// keep count of # of bytes read => N
ubuff = (u_int32_t*) calloc(N, sizeof(u_int32_t));  // calloc initializes to 0
u8_toucs(ubuff, N, buff, N);
// ubuff now is an array of 4-byte integers representing
// a Unicode character each

当然,如果多个字节表示一个字符,则文件中的Unicode字符数完全可能少于n。这意味着4*n内存分配过多。在这种情况下,ubuff的块将是0(Unicode Null字符)。因此,我只需扫描阵列并根据需要重新分配内存:

u_int32_t* original = ubuff;
int sz=0;
while *ubuff != 0 {
    ubuff++;
    sz++;
}
ubuff = realloc(original, sizeof(*original) * i);

注意:如果您得到关于u_int32_t的类型错误,请将typedef uint32_t u_int32_t;放在代码的开头。

最新更新