将 UTF8 字符文字分配给 char16_t - 字符常量中的字符太多
Assigning UTF8 char literal to char16_t - too many chars in char constant
我正在为嵌入式系统创建 UTF8 table 查找。 table用于将UTF8编码的字符转换为字体(数组)中的位图索引。
我收到警告“多字符字符文字(潜在的可移植性问题)”。 “conversion_table”数组中的每个条目都带有此警告标记。
代码如下:
typedef struct UTF8_To_Bitmap_Index_s
{
char16_t encoded_character;
uint8_t bitmap_index;
} UTF8_To_Bitmap_Index_t;
size_t width_wchar_t = sizeof(wchar_t);
UTF8_To_Bitmap_Index_t conversion_table[] =
{
{'¡', 0x00},
{'À', 0x00},
{'Á', 0x00},
{'Ã', 0x00},
{'Ä', 0x00},
{'Å', 0x00},
{'Ç', 0x00},
{'É', 0x00},
{'Í', 0x00},
{'Ó', 0x00},
{'Õ', 0x00},
{'Ö', 0x00},
{'Ø', 0x00},
{'Ú', 0x00},
{'Ü', 0x00},
{'ß', 0x00},
{'à', 0x00},
{'á', 0x00},
{'â', 0x00},
{'ã', 0x00},
{'ä', 0x00},
{'å', 0x00},
{'æ', 0x00},
{'ç', 0x00},
{'è', 0x00},
{'é', 0x00},
{'ê', 0x00},
{'í', 0x00},
{'ñ', 0x00},
{'ó', 0x00},
{'ô', 0x00},
};
有什么方法可以修改上面的代码来消除警告吗?
(注意:在确定实际位图索引之前,0x00
是一个占位符。)
生成的数据正确:
50 UTF8_To_Bitmap_Index_t conversion_table[] =
\ conversion_table:
\ 00000000 0xC2A1 DC16 49825
\ 00000002 0x00 0x00 DC8 0, 0
\ 00000004 0xC380 DC16 50048
\ 00000006 0x00 0x00 DC8 0, 0
\ 00000008 0xC381 DC16 50049
\ 0000000A 0x00 0x00 DC8 0, 0
\ 0000000C 0xC383 DC16 50051
\ 0000000E 0x00 0x00 DC8 0, 0
\ 00000010 0xC384 DC16 50052
\ 00000012 0x00 0x00 DC8 0, 0
\ 00000014 0xC385 DC16 50053
\ 00000016 0x00 0x00 DC8 0, 0
\ 00000018 0xC387 DC16 50055
\ 0000001A 0x00 0x00 DC8 0, 0
\ 0000001C 0xC389 DC16 50057
\ 0000001E 0x00 0x00 DC8 0, 0
\ 00000020 0xC38D DC16 50061
\ 00000022 0x00 0x00 DC8 0, 0
\ 00000024 0xC393 DC16 50067
\ 00000026 0x00 0x00 DC8 0, 0
\ 00000028 0xC395 DC16 50069
\ 0000002A 0x00 0x00 DC8 0, 0
\ 0000002C 0xC396 DC16 50070
\ 0000002E 0x00 0x00 DC8 0, 0
\ 00000030 0xC398 DC16 50072
\ 00000032 0x00 0x00 DC8 0, 0
\ 00000034 0xC39A DC16 50074
\ 00000036 0x00 0x00 DC8 0, 0
\ 00000038 0xC39C DC16 50076
\ 0000003A 0x00 0x00 DC8 0, 0
\ 0000003C 0xC39F DC16 50079
\ 0000003E 0x00 0x00 DC8 0, 0
\ 00000040 0xC3A0 DC16 50080
\ 00000042 0x00 0x00 DC8 0, 0
\ 00000044 0xC3A1 DC16 50081
\ 00000046 0x00 0x00 DC8 0, 0
\ 00000048 0xC3A2 DC16 50082
\ 0000004A 0x00 0x00 DC8 0, 0
\ 0000004C 0xC3A3 DC16 50083
\ 0000004E 0x00 0x00 DC8 0, 0
\ 00000050 0xC3A4 DC16 50084
\ 00000052 0x00 0x00 DC8 0, 0
\ 00000054 0xC3A5 DC16 50085
\ 00000056 0x00 0x00 DC8 0, 0
\ 00000058 0xC3A6 DC16 50086
\ 0000005A 0x00 0x00 DC8 0, 0
\ 0000005C 0xC3A7 DC16 50087
\ 0000005E 0x00 0x00 DC8 0, 0
\ 00000060 0xC3A8 DC16 50088
\ 00000062 0x00 0x00 DC8 0, 0
\ 00000064 0xC3A9 DC16 50089
\ 00000066 0x00 0x00 DC8 0, 0
\ 00000068 0xC3AA DC16 50090
\ 0000006A 0x00 0x00 DC8 0, 0
\ 0000006C 0xC3AD DC16 50093
\ 0000006E 0x00 0x00 DC8 0, 0
\ 00000070 0xC3B1 DC16 50097
\ 00000072 0x00 0x00 DC8 0, 0
\ 00000074 0xC3B3 DC16 50099
\ 00000076 0x00 0x00 DC8 0, 0
\ 00000078 0xC3B4 DC16 50100
\ 0000007A 0x00 0x00 DC8 0, 0
资源:
编译器——IAR 嵌入式 Workbench 版本 7.4
目标平台:ARM Cortex M
根据标准(§6.4.4.4.2 和 §6.4.4.4.10),代码原样不可移植:
An integer character constant is a sequence of one or more multibyte characters enclosed in single-quotes, as in 'x'. A wide character constant is the same, except prefixed by the letter L, u, or U.
... The value of an integer character constant containing more than one character (e.g., 'ab'), […] is implementation-defined. ...
您正在将字符编码为 char16_t
,而且根据标准,您不应使用 ' '
语法,而应使用 u' '
语法:
这应该可以解决您的问题:
UTF8_To_Bitmap_Index_t conversion_table[] =
{
{u'¡', 0x00},
{u'À', 0x00},
...
尝试将 UTF-8 编码的字节序列存储在 char16_t
中基本上是不正确的,即使它适合(并且通常不能保证这一点,因为 UTF-8 代码序列可以是从一到四个字节长)。 char16_t
的预期目的是存储单个 UTF-16 代码值(不一定是整个字符,但这是另一回事)。 [注1]
当然,16 位就是 16 位,所以如果您真的愿意,可以将两个八位字节混成一个 char16_t
。但是不要指望编译器会在没有警告的情况下接受它。
如果您绝对知道 UTF-8 序列是两个字节长,那么您应该将它存储在 char[2]
中。如果您希望能够将这两个字符称为标量,则可以将 char[2]
与 char16_t
输入双关语,但严格的别名规则可能会妨碍您。此外,您需要仔细考虑您目前刚刚忽略的字节顺序问题。
当您从串行端口(或 UTF-8 编码的文件或套接字,或其他任何东西)接收到 UTF-8 编码的序列时,您将首先接收到第一个字节,这是理所当然的。如果将这些字符中的两个映射到一个双字节整数,则整数的低地址字节将包含第一个字节,整数的高地址字节将包含第二个字节。如果您使用高位字节具有低位地址的大端架构,那就太完美了。也许您正在大端环境中工作。但如果没有,您可能会发现您的输入与您创建的常量不匹配。
如您看到的警告所示,没有标准方法可以将双字节序列转换为整数(请记住,在 C 中,字符文字是 int
,而不是char
)。因此,给定的编译器可能会做任何事情,包括将字符文字限制为单个字节,但编译器通常将多个字符编码为 base-256 数字。因此,'AB'
和 \x4142
都产生整数 0x4142
。但是如果你要将该整数映射到小端机器上的 char[4]
,你将看到的是字节序列 0x42 0x41 0x00 0x00
,如果你将它打印到控制台,它将显示为 BA
.
根据您如何生成用于查找的双字节密钥 table,这可能会或可能不会给您想要的结果。无论如何,它不会成为 portable(甚至是面向未来的),因为没有标准机制可以从两字节的 UTF-8 编码中创建 16 位编译时整数。
不过,这个谜题还有一部分。您的程序似乎包含以下内容:
{'ß', 0x00},
但我们知道(即使为了简单起见我们宁愿忽略这一事实)计算机内部不存在字符之类的东西。您会发现所有这些都是 0 和 1。如果我们真的很准确,你也不会找到那些,因为在串行总线内没有从一个电极到另一个电极的微观零点。相反,有些亚原子现象可以被视为适合两种不同的状态。但是我们不需要下降到那种物理描述水平;可以说保存程序的文件不包含小字符而是包含位序列就足够了。问题是,究竟有哪些比特序列?特别是,哪些(以及多少)位显示为 ß
?答案由文件的字符编码定义。
我的猜测是您使用使用 UTF-8 编码的编辑器编写了该源文件,因此 ß
显示为两个字节序列 C3 9F
。现在,当编译器看到这两个字节时会发生什么?
C 标准不需要任何特定的编码,但它允许编译器将其输入视为单字节字符序列,每个字符代表基本源字符集中的字符,不包括ß
。编译器对于如何处理任何与源字符集中的字符不对应的字节,以及如何将这些字节映射到 executable 中的字符和字符串(这是允许的使用与源文件不同的编码。)这一切都变得有点复杂;也许我稍后会添加完整的解释。可以这么说,许多编译器只是将一个字节视为一个字节,至少在字符和字符串文字中是这样;字节只是通过而不考虑编码。 (其他编译器使用更复杂的算法,考虑到源代码和执行编码,这可能会有所不同。但在简单的情况下,结果是相同的。)
所以这就是编译器抱怨 'ß'
不止一个字符的原因:它是,因为它被编码为两个字节。 (如果您使用 Latin-1 作为源字符集和执行字符集,那么 ß
将只是一个字节,0xDF,并且编译器不会抱怨。但这不会让您得到一个 UTF- 8转换table.)
C11(和当代的C++版本)特权Unicode和UTF-8传输编码,这是完全合适的。它通过提供一种允许您使用基本源字符集明确指定 Unicode 字符代码的语法,并通过提供描述所需编码的字符串和字符文字前缀来解决多个区域设置的一些混乱。如果您有这样的编译器,您可以将 ß 写成 \u00DF
,这是它的 Unicode 代码点,并使用 u8
前缀将其包含在 UTF-8 字符串文字中:u8"\u00DF"
. [注2]
备注
从技术上讲,如果预处理器宏 __STDC_UTF_16__
在 uchar.h
中定义,则 char16_t
仅使用 UTF-16 标识,对于 char32_t
也是如此和 __STDC_UTF_32__
。但我仍然认为可以公平地说,预期用途是 Unicode 编码。
如果您想使用 UTF-16 或 UTF-32 编码,您可以通过编写 u"\u00DF"
或 char32_t[]
来创建 char16_t[]
字符串文字字符串文字,U"\u00DF"
。这两个都有两个元素,包括 NUL 终止符。 (其中一个可能与宽字符字符串文字 L"\u00DF"
相同,但这取决于配置的执行区域设置和编译器支持。)您还可以使用 char16_t
和 char32_t
字符文字。但请注意 u'\u00DF'
的值为 0xDF
,这是 ß 的 Unicode 代码点。
我正在为嵌入式系统创建 UTF8 table 查找。 table用于将UTF8编码的字符转换为字体(数组)中的位图索引。
我收到警告“多字符字符文字(潜在的可移植性问题)”。 “conversion_table”数组中的每个条目都带有此警告标记。
代码如下:
typedef struct UTF8_To_Bitmap_Index_s
{
char16_t encoded_character;
uint8_t bitmap_index;
} UTF8_To_Bitmap_Index_t;
size_t width_wchar_t = sizeof(wchar_t);
UTF8_To_Bitmap_Index_t conversion_table[] =
{
{'¡', 0x00},
{'À', 0x00},
{'Á', 0x00},
{'Ã', 0x00},
{'Ä', 0x00},
{'Å', 0x00},
{'Ç', 0x00},
{'É', 0x00},
{'Í', 0x00},
{'Ó', 0x00},
{'Õ', 0x00},
{'Ö', 0x00},
{'Ø', 0x00},
{'Ú', 0x00},
{'Ü', 0x00},
{'ß', 0x00},
{'à', 0x00},
{'á', 0x00},
{'â', 0x00},
{'ã', 0x00},
{'ä', 0x00},
{'å', 0x00},
{'æ', 0x00},
{'ç', 0x00},
{'è', 0x00},
{'é', 0x00},
{'ê', 0x00},
{'í', 0x00},
{'ñ', 0x00},
{'ó', 0x00},
{'ô', 0x00},
};
有什么方法可以修改上面的代码来消除警告吗?
(注意:在确定实际位图索引之前,0x00
是一个占位符。)
生成的数据正确:
50 UTF8_To_Bitmap_Index_t conversion_table[] =
\ conversion_table:
\ 00000000 0xC2A1 DC16 49825
\ 00000002 0x00 0x00 DC8 0, 0
\ 00000004 0xC380 DC16 50048
\ 00000006 0x00 0x00 DC8 0, 0
\ 00000008 0xC381 DC16 50049
\ 0000000A 0x00 0x00 DC8 0, 0
\ 0000000C 0xC383 DC16 50051
\ 0000000E 0x00 0x00 DC8 0, 0
\ 00000010 0xC384 DC16 50052
\ 00000012 0x00 0x00 DC8 0, 0
\ 00000014 0xC385 DC16 50053
\ 00000016 0x00 0x00 DC8 0, 0
\ 00000018 0xC387 DC16 50055
\ 0000001A 0x00 0x00 DC8 0, 0
\ 0000001C 0xC389 DC16 50057
\ 0000001E 0x00 0x00 DC8 0, 0
\ 00000020 0xC38D DC16 50061
\ 00000022 0x00 0x00 DC8 0, 0
\ 00000024 0xC393 DC16 50067
\ 00000026 0x00 0x00 DC8 0, 0
\ 00000028 0xC395 DC16 50069
\ 0000002A 0x00 0x00 DC8 0, 0
\ 0000002C 0xC396 DC16 50070
\ 0000002E 0x00 0x00 DC8 0, 0
\ 00000030 0xC398 DC16 50072
\ 00000032 0x00 0x00 DC8 0, 0
\ 00000034 0xC39A DC16 50074
\ 00000036 0x00 0x00 DC8 0, 0
\ 00000038 0xC39C DC16 50076
\ 0000003A 0x00 0x00 DC8 0, 0
\ 0000003C 0xC39F DC16 50079
\ 0000003E 0x00 0x00 DC8 0, 0
\ 00000040 0xC3A0 DC16 50080
\ 00000042 0x00 0x00 DC8 0, 0
\ 00000044 0xC3A1 DC16 50081
\ 00000046 0x00 0x00 DC8 0, 0
\ 00000048 0xC3A2 DC16 50082
\ 0000004A 0x00 0x00 DC8 0, 0
\ 0000004C 0xC3A3 DC16 50083
\ 0000004E 0x00 0x00 DC8 0, 0
\ 00000050 0xC3A4 DC16 50084
\ 00000052 0x00 0x00 DC8 0, 0
\ 00000054 0xC3A5 DC16 50085
\ 00000056 0x00 0x00 DC8 0, 0
\ 00000058 0xC3A6 DC16 50086
\ 0000005A 0x00 0x00 DC8 0, 0
\ 0000005C 0xC3A7 DC16 50087
\ 0000005E 0x00 0x00 DC8 0, 0
\ 00000060 0xC3A8 DC16 50088
\ 00000062 0x00 0x00 DC8 0, 0
\ 00000064 0xC3A9 DC16 50089
\ 00000066 0x00 0x00 DC8 0, 0
\ 00000068 0xC3AA DC16 50090
\ 0000006A 0x00 0x00 DC8 0, 0
\ 0000006C 0xC3AD DC16 50093
\ 0000006E 0x00 0x00 DC8 0, 0
\ 00000070 0xC3B1 DC16 50097
\ 00000072 0x00 0x00 DC8 0, 0
\ 00000074 0xC3B3 DC16 50099
\ 00000076 0x00 0x00 DC8 0, 0
\ 00000078 0xC3B4 DC16 50100
\ 0000007A 0x00 0x00 DC8 0, 0
资源:
编译器——IAR 嵌入式 Workbench 版本 7.4
目标平台:ARM Cortex M
根据标准(§6.4.4.4.2 和 §6.4.4.4.10),代码原样不可移植:
An integer character constant is a sequence of one or more multibyte characters enclosed in single-quotes, as in 'x'. A wide character constant is the same, except prefixed by the letter L, u, or U. ... The value of an integer character constant containing more than one character (e.g., 'ab'), […] is implementation-defined. ...
您正在将字符编码为 char16_t
,而且根据标准,您不应使用 ' '
语法,而应使用 u' '
语法:
这应该可以解决您的问题:
UTF8_To_Bitmap_Index_t conversion_table[] =
{
{u'¡', 0x00},
{u'À', 0x00},
...
尝试将 UTF-8 编码的字节序列存储在 char16_t
中基本上是不正确的,即使它适合(并且通常不能保证这一点,因为 UTF-8 代码序列可以是从一到四个字节长)。 char16_t
的预期目的是存储单个 UTF-16 代码值(不一定是整个字符,但这是另一回事)。 [注1]
当然,16 位就是 16 位,所以如果您真的愿意,可以将两个八位字节混成一个 char16_t
。但是不要指望编译器会在没有警告的情况下接受它。
如果您绝对知道 UTF-8 序列是两个字节长,那么您应该将它存储在 char[2]
中。如果您希望能够将这两个字符称为标量,则可以将 char[2]
与 char16_t
输入双关语,但严格的别名规则可能会妨碍您。此外,您需要仔细考虑您目前刚刚忽略的字节顺序问题。
当您从串行端口(或 UTF-8 编码的文件或套接字,或其他任何东西)接收到 UTF-8 编码的序列时,您将首先接收到第一个字节,这是理所当然的。如果将这些字符中的两个映射到一个双字节整数,则整数的低地址字节将包含第一个字节,整数的高地址字节将包含第二个字节。如果您使用高位字节具有低位地址的大端架构,那就太完美了。也许您正在大端环境中工作。但如果没有,您可能会发现您的输入与您创建的常量不匹配。
如您看到的警告所示,没有标准方法可以将双字节序列转换为整数(请记住,在 C 中,字符文字是 int
,而不是char
)。因此,给定的编译器可能会做任何事情,包括将字符文字限制为单个字节,但编译器通常将多个字符编码为 base-256 数字。因此,'AB'
和 \x4142
都产生整数 0x4142
。但是如果你要将该整数映射到小端机器上的 char[4]
,你将看到的是字节序列 0x42 0x41 0x00 0x00
,如果你将它打印到控制台,它将显示为 BA
.
根据您如何生成用于查找的双字节密钥 table,这可能会或可能不会给您想要的结果。无论如何,它不会成为 portable(甚至是面向未来的),因为没有标准机制可以从两字节的 UTF-8 编码中创建 16 位编译时整数。
不过,这个谜题还有一部分。您的程序似乎包含以下内容:
{'ß', 0x00},
但我们知道(即使为了简单起见我们宁愿忽略这一事实)计算机内部不存在字符之类的东西。您会发现所有这些都是 0 和 1。如果我们真的很准确,你也不会找到那些,因为在串行总线内没有从一个电极到另一个电极的微观零点。相反,有些亚原子现象可以被视为适合两种不同的状态。但是我们不需要下降到那种物理描述水平;可以说保存程序的文件不包含小字符而是包含位序列就足够了。问题是,究竟有哪些比特序列?特别是,哪些(以及多少)位显示为 ß
?答案由文件的字符编码定义。
我的猜测是您使用使用 UTF-8 编码的编辑器编写了该源文件,因此 ß
显示为两个字节序列 C3 9F
。现在,当编译器看到这两个字节时会发生什么?
C 标准不需要任何特定的编码,但它允许编译器将其输入视为单字节字符序列,每个字符代表基本源字符集中的字符,不包括ß
。编译器对于如何处理任何与源字符集中的字符不对应的字节,以及如何将这些字节映射到 executable 中的字符和字符串(这是允许的使用与源文件不同的编码。)这一切都变得有点复杂;也许我稍后会添加完整的解释。可以这么说,许多编译器只是将一个字节视为一个字节,至少在字符和字符串文字中是这样;字节只是通过而不考虑编码。 (其他编译器使用更复杂的算法,考虑到源代码和执行编码,这可能会有所不同。但在简单的情况下,结果是相同的。)
所以这就是编译器抱怨 'ß'
不止一个字符的原因:它是,因为它被编码为两个字节。 (如果您使用 Latin-1 作为源字符集和执行字符集,那么 ß
将只是一个字节,0xDF,并且编译器不会抱怨。但这不会让您得到一个 UTF- 8转换table.)
C11(和当代的C++版本)特权Unicode和UTF-8传输编码,这是完全合适的。它通过提供一种允许您使用基本源字符集明确指定 Unicode 字符代码的语法,并通过提供描述所需编码的字符串和字符文字前缀来解决多个区域设置的一些混乱。如果您有这样的编译器,您可以将 ß 写成 \u00DF
,这是它的 Unicode 代码点,并使用 u8
前缀将其包含在 UTF-8 字符串文字中:u8"\u00DF"
. [注2]
备注
从技术上讲,如果预处理器宏
__STDC_UTF_16__
在uchar.h
中定义,则char16_t
仅使用 UTF-16 标识,对于char32_t
也是如此和__STDC_UTF_32__
。但我仍然认为可以公平地说,预期用途是 Unicode 编码。如果您想使用 UTF-16 或 UTF-32 编码,您可以通过编写
u"\u00DF"
或char32_t[]
来创建char16_t[]
字符串文字字符串文字,U"\u00DF"
。这两个都有两个元素,包括 NUL 终止符。 (其中一个可能与宽字符字符串文字L"\u00DF"
相同,但这取决于配置的执行区域设置和编译器支持。)您还可以使用char16_t
和char32_t
字符文字。但请注意u'\u00DF'
的值为0xDF
,这是 ß 的 Unicode 代码点。