文字 UTF-8 字符的数值
Numeric value of literal UTF-8 characters
我正在研究一个字符串转义函数,它将像 \uxxxx
(其中 xxxx
是一个十六进制值)这样的文字序列转换为相应值的字节。我打算让函数获取 xxxx
序列的前两个字符,计算字节值,并与第二个序列相同。
但是我 运行 使用文字类型的 UTF-8 字符得到了意想不到的结果。以下说明了我的问题:
#include <stdio.h>
int main()
{
unsigned char *str1 = "abcĢ";
unsigned char *str2 = "abc\x01\x22";
for (unsigned i = 0; i < 5; i++)
printf ("String 1 character #%u: %x\n", i, str1[i]);
for (unsigned i = 0; i < 5; i++)
printf ("String 2 character #%u: %x\n", i, str2[i]);
return 0;
}
输出:
String 1 character #0: 61
String 1 character #1: 62
String 1 character #2: 63
String 1 character #3: c4
String 1 character #4: a2
String 2 character #0: 61
String 2 character #1: 62
String 2 character #2: 63
String 2 character #3: 1
String 2 character #4: 22
Unicode 字符 Ģ
的十六进制值为 \x0122
,因此我希望字节 #3 和 #4 分别为 \x01
和 x22
。
c4
和a2
从何而来?我想我不明白字符串中的多字节字符是如何在 C 中编码的。任何帮助将不胜感激。
Unicode character Ģ
has e hex value of \x0122
, so I would expect bytes #3 and #4 to be \x01
and \x22
respectively.
Where do c4
and a2
come from?
在 Unicode 中,Ģ
是代码点 U+0122 LATIN CAPITAL LETTER G WITH CEDILLA
,在 UTF-8 中编码为字节 0xC4 0xA2
。
您的源文件保存为 UTF-8,或者您的编译器配置为以 UTF-8 保存字符串文字。无论哪种方式,在您的 str1
字符串中,文字 Ģ
都存储为 UTF-8。因此:
unsigned char *str1 = "abcĢ";
大致相当于:
unsigned char literal[] = {'a', 'b', 'c', 0xC4, 0xA2, '[=11=]'};
unsigned char *str1 = &literal[0];
在转义序列中,整个序列表示一个单个数值。因此,\x01
和 \x22
分别表示单独的数值 0x01
十六进制(1
十进制)和 0x22
十六进制(34
十进制)。因此:
unsigned char *str2 = "abc\x01\x22";
大致相当于:
unsigned char literal[] = {'a', 'b', 'c', 0x01, 0x22, '[=13=]'};
unsigned char *str2 = &literal[0];
您只是输出 str1
和 str2
指向的字符串的原始字节。
转义序列\u0122
表示数值0x0122
十六进制(290
十进制),在Unicode中是代码点U+0122
,因此在C4 A2
中UTF-8。所以,如果你有这样的输入字符串:
const char *str = "abc\u0122"; // {'a', 'b', 'c', '\', 'u', '0', '1', '2', '2', '[=14=]'}
而你想解码为UTF-8,你需要检测"\u"
前缀,提取下面的"0122"
子串,将其作为十六进制数解析为整数,解释该整数作为 Unicode 代码点,并将其转换为 UTF-8(a
、b
和 c
已经是 UTF-8 中的有效字符)。
UTF-8 不能以将大值分解为单个字节的简单方式工作,因为它会产生歧义。怎么区分"\u4142" (䅂)
和两个字符串"AB"
?
从 Unicode 代码点数字生成 UTF-8 字节字符串的规则非常简单,并且消除了歧义。给定任何字节值序列,它要么定义明确的代码点,要么是无效序列。
这是一个将单个 Unicode 代码点值转换为 UTF-8 字节序列的简单函数。
void codepoint_to_UTF8(int codepoint, char * out)
/* out must point to a buffer of at least 5 chars. */
{
if (codepoint <= 0x7f)
*out++ = (char)codepoint;
else if (codepoint <= 0x7ff)
{
*out++ = (char)(0xc0 | ((codepoint >> 6) & 0x1f));
*out++ = (char)(0x80 | (codepoint & 0x3f));
}
else if (codepoint <= 0xffff)
{
*out++ = (char)(0xe0 | ((codepoint >> 12) & 0x0f));
*out++ = (char)(0x80 | ((codepoint >> 6) & 0x3f));
*out++ = (char)(0x80 | (codepoint & 0x3f));
}
else
{
*out++ = (char)(0xf0 | ((codepoint >> 18) & 0x07));
*out++ = (char)(0x80 | ((codepoint >> 12) & 0x3f));
*out++ = (char)(0x80 | ((codepoint >> 6) & 0x3f));
*out++ = (char)(0x80 | (codepoint & 0x3f));
}
*out = 0;
}
请注意,此函数不进行错误检查,因此如果您为其提供的输入超出 0 到 0x10ffff 的有效 Unicode 范围,它将生成不正确(但仍然有效)的 UTF-8 序列。
我正在研究一个字符串转义函数,它将像 \uxxxx
(其中 xxxx
是一个十六进制值)这样的文字序列转换为相应值的字节。我打算让函数获取 xxxx
序列的前两个字符,计算字节值,并与第二个序列相同。
但是我 运行 使用文字类型的 UTF-8 字符得到了意想不到的结果。以下说明了我的问题:
#include <stdio.h>
int main()
{
unsigned char *str1 = "abcĢ";
unsigned char *str2 = "abc\x01\x22";
for (unsigned i = 0; i < 5; i++)
printf ("String 1 character #%u: %x\n", i, str1[i]);
for (unsigned i = 0; i < 5; i++)
printf ("String 2 character #%u: %x\n", i, str2[i]);
return 0;
}
输出:
String 1 character #0: 61
String 1 character #1: 62
String 1 character #2: 63
String 1 character #3: c4
String 1 character #4: a2
String 2 character #0: 61
String 2 character #1: 62
String 2 character #2: 63
String 2 character #3: 1
String 2 character #4: 22
Unicode 字符 Ģ
的十六进制值为 \x0122
,因此我希望字节 #3 和 #4 分别为 \x01
和 x22
。
c4
和a2
从何而来?我想我不明白字符串中的多字节字符是如何在 C 中编码的。任何帮助将不胜感激。
Unicode character
Ģ
has e hex value of\x0122
, so I would expect bytes #3 and #4 to be\x01
and\x22
respectively.Where do
c4
anda2
come from?
在 Unicode 中,Ģ
是代码点 U+0122 LATIN CAPITAL LETTER G WITH CEDILLA
,在 UTF-8 中编码为字节 0xC4 0xA2
。
您的源文件保存为 UTF-8,或者您的编译器配置为以 UTF-8 保存字符串文字。无论哪种方式,在您的 str1
字符串中,文字 Ģ
都存储为 UTF-8。因此:
unsigned char *str1 = "abcĢ";
大致相当于:
unsigned char literal[] = {'a', 'b', 'c', 0xC4, 0xA2, '[=11=]'};
unsigned char *str1 = &literal[0];
在转义序列中,整个序列表示一个单个数值。因此,\x01
和 \x22
分别表示单独的数值 0x01
十六进制(1
十进制)和 0x22
十六进制(34
十进制)。因此:
unsigned char *str2 = "abc\x01\x22";
大致相当于:
unsigned char literal[] = {'a', 'b', 'c', 0x01, 0x22, '[=13=]'};
unsigned char *str2 = &literal[0];
您只是输出 str1
和 str2
指向的字符串的原始字节。
转义序列\u0122
表示数值0x0122
十六进制(290
十进制),在Unicode中是代码点U+0122
,因此在C4 A2
中UTF-8。所以,如果你有这样的输入字符串:
const char *str = "abc\u0122"; // {'a', 'b', 'c', '\', 'u', '0', '1', '2', '2', '[=14=]'}
而你想解码为UTF-8,你需要检测"\u"
前缀,提取下面的"0122"
子串,将其作为十六进制数解析为整数,解释该整数作为 Unicode 代码点,并将其转换为 UTF-8(a
、b
和 c
已经是 UTF-8 中的有效字符)。
UTF-8 不能以将大值分解为单个字节的简单方式工作,因为它会产生歧义。怎么区分"\u4142" (䅂)
和两个字符串"AB"
?
从 Unicode 代码点数字生成 UTF-8 字节字符串的规则非常简单,并且消除了歧义。给定任何字节值序列,它要么定义明确的代码点,要么是无效序列。
这是一个将单个 Unicode 代码点值转换为 UTF-8 字节序列的简单函数。
void codepoint_to_UTF8(int codepoint, char * out)
/* out must point to a buffer of at least 5 chars. */
{
if (codepoint <= 0x7f)
*out++ = (char)codepoint;
else if (codepoint <= 0x7ff)
{
*out++ = (char)(0xc0 | ((codepoint >> 6) & 0x1f));
*out++ = (char)(0x80 | (codepoint & 0x3f));
}
else if (codepoint <= 0xffff)
{
*out++ = (char)(0xe0 | ((codepoint >> 12) & 0x0f));
*out++ = (char)(0x80 | ((codepoint >> 6) & 0x3f));
*out++ = (char)(0x80 | (codepoint & 0x3f));
}
else
{
*out++ = (char)(0xf0 | ((codepoint >> 18) & 0x07));
*out++ = (char)(0x80 | ((codepoint >> 12) & 0x3f));
*out++ = (char)(0x80 | ((codepoint >> 6) & 0x3f));
*out++ = (char)(0x80 | (codepoint & 0x3f));
}
*out = 0;
}
请注意,此函数不进行错误检查,因此如果您为其提供的输入超出 0 到 0x10ffff 的有效 Unicode 范围,它将生成不正确(但仍然有效)的 UTF-8 序列。