如何获取 C 中 char * 下存储的多字节字符的 unicode 值?
How to get unicode value of multibyte character stored under char * in C?
假设我不使用 C11 中的 <uchar.h>
而有这样的东西
char *a = "Ā";
我怎样才能得到这个字符的 unicode 值(它是 256)?做这样的事情:
int *a_value = (int *)a;
printf("%d\n", *a_value);
无效。
这个字符在内存中是怎么写的?
gdb 显示:
loc a = 0x555555556004 "Ā": -60 '4'
但完全不明白它到底是什么意思。
我检查了 a
的大小,它是 2 个字节,这没问题,但是
printf("%d\n", a[0]);
printf("%d\n", a[1]);
也不行。它给了我 -60 和 -128。
值编码为 UTF-8。
256
在二进制中是 100000000
(9 位)。它多于 7 位(但少于 12 位),因此需要 2 个字节以 UTF-8 编码。
第一个字节将包含前 5 位,第二个字节将包含最后 6 位。
所以,11 位的二进制 256
又是 00100000000
或 00100
后跟 000000
最终 UTF-8 第一个字节 11000100
... 110
+ 00100
最终 UTF-8 第二字节 10000000
... 10
+ 000000
11000100
十进制为196
,或者考虑MSB为符号位:-60
10000000
十进制为 128
,或者考虑 MSB 为符号位:-128
中阅读有关 UTF-8 编码的更多信息
还有两件事:
(1) 你得到那些奇怪的数字是因为你机器上的普通字符(像许多字符一样)显然是有符号的。您可以通过转换为 unsigned char
:
来查看“真实”字节
char *a = "Ā";
printf("%u %u\n", ((unsigned char *)a)[0], ((unsigned char *)a)[1]);
printf("%x %x\n", ((unsigned char *)a)[0], ((unsigned char *)a)[1]);
或一直使用 unsigned char
:
unsigned char *u = "Ā";
printf("%x %x\n", u[0], u[1]);
%u
版本打印196 128
,%x
版本打印c4 80
.
(2) 我不确定你所说的“不使用 C11 中的 <uchar.h>
”是什么意思,但是如果你不想手动进行 UTF-8 转换,你可以转换一个通过使用来自 <stdlib.h>
:
的库函数 mbtowc
将“多字节字符串”(几乎总是 UTF-8)转换为宽字符或 Unicode 字符
wchar_t wc;
mbtowc(&wc, a, strlen(a));
printf("%d %x\n", wc, wc);
这会在我的机器上打印 256 100
,因为 Ā
是 U+0100。
另一个有用的函数是 mbstowcs
,它可以同时处理多个字符:
char *mbs = "Daß ist sehr schön";
printf("%s\n", mbs);
wchar_t wcs[20];
int n = mbstowcs(wcs, mbs, 20);
for(int i = 0; i < n; i++)
printf("%3d %x %lc\n", wcs[i], wcs[i], wcs[i]);
但是,当使用 mbtowc
和 mbstowcs
等函数时,您必须记住它们 而不是 必须处理 UTF-8 和 Unicode。除了 Unicode,还有宽字符编码,除了 UTF-8,还有多字节表示。事实上,为了让这些功能在我的机器上“正确”工作,我必须先调用
setlocale(LC_CTYPE, "");
告诉他们可以使用我的语言环境设置(即 en_US.UTF-8
),而不是 不 假定 Unicode 的默认“C”语言环境。
假设我不使用 C11 中的 <uchar.h>
而有这样的东西
char *a = "Ā";
我怎样才能得到这个字符的 unicode 值(它是 256)?做这样的事情:
int *a_value = (int *)a;
printf("%d\n", *a_value);
无效。
这个字符在内存中是怎么写的? gdb 显示:
loc a = 0x555555556004 "Ā": -60 '4'
但完全不明白它到底是什么意思。
我检查了 a
的大小,它是 2 个字节,这没问题,但是
printf("%d\n", a[0]);
printf("%d\n", a[1]);
也不行。它给了我 -60 和 -128。
值编码为 UTF-8。
256
在二进制中是 100000000
(9 位)。它多于 7 位(但少于 12 位),因此需要 2 个字节以 UTF-8 编码。
第一个字节将包含前 5 位,第二个字节将包含最后 6 位。
所以,11 位的二进制 256
又是 00100000000
或 00100
后跟 000000
最终 UTF-8 第一个字节 11000100
... 110
+ 00100
最终 UTF-8 第二字节 10000000
... 10
+ 000000
11000100
十进制为196
,或者考虑MSB为符号位:-60
10000000
十进制为 128
,或者考虑 MSB 为符号位:-128
还有两件事:
(1) 你得到那些奇怪的数字是因为你机器上的普通字符(像许多字符一样)显然是有符号的。您可以通过转换为 unsigned char
:
char *a = "Ā";
printf("%u %u\n", ((unsigned char *)a)[0], ((unsigned char *)a)[1]);
printf("%x %x\n", ((unsigned char *)a)[0], ((unsigned char *)a)[1]);
或一直使用 unsigned char
:
unsigned char *u = "Ā";
printf("%x %x\n", u[0], u[1]);
%u
版本打印196 128
,%x
版本打印c4 80
.
(2) 我不确定你所说的“不使用 C11 中的 <uchar.h>
”是什么意思,但是如果你不想手动进行 UTF-8 转换,你可以转换一个通过使用来自 <stdlib.h>
:
mbtowc
将“多字节字符串”(几乎总是 UTF-8)转换为宽字符或 Unicode 字符
wchar_t wc;
mbtowc(&wc, a, strlen(a));
printf("%d %x\n", wc, wc);
这会在我的机器上打印 256 100
,因为 Ā
是 U+0100。
另一个有用的函数是 mbstowcs
,它可以同时处理多个字符:
char *mbs = "Daß ist sehr schön";
printf("%s\n", mbs);
wchar_t wcs[20];
int n = mbstowcs(wcs, mbs, 20);
for(int i = 0; i < n; i++)
printf("%3d %x %lc\n", wcs[i], wcs[i], wcs[i]);
但是,当使用 mbtowc
和 mbstowcs
等函数时,您必须记住它们 而不是 必须处理 UTF-8 和 Unicode。除了 Unicode,还有宽字符编码,除了 UTF-8,还有多字节表示。事实上,为了让这些功能在我的机器上“正确”工作,我必须先调用
setlocale(LC_CTYPE, "");
告诉他们可以使用我的语言环境设置(即 en_US.UTF-8
),而不是 不 假定 Unicode 的默认“C”语言环境。