我正在尝试从文件中读取和操作乌尔都语文本。然而,似乎一个字符并没有被完整地读入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;
放在代码的开头。