通过将 char 转换为 USHORT 得到错误的 UTF-8 值
Getting wrong UTF-8 values by casting char into USHORT
这是我的第一个问题,如果我遗漏了重要规则,请随时批评或纠正我。
最近我的任务是将旧的 DOS C 代码移植到 Linux 平台。字体处理由位字体实现。我编写了一个函数,如果您将正确的 Unicode 值传递给它,它能够绘制选定的字形。
但是,如果我尝试将 char 转换为 USHORT(函数需要这种类型),当字符在 ASCII-table.
之外时,我会得到错误的值
char* test;
test = "°";
printf("test: %hu\n",(USHORT)test[0]);
显示的数字(控制台)应该是 176 而不是 194。
如果你使用“!”将显示正确的值 33。
我通过设置 GCC 编译器标志
确保 char 是无符号的
-unsigned-char
GCC 编译器默认使用 UTF-8 编码。我现在真的不知道问题出在哪里。
我是否需要向编译器添加另一个标志?
更新
在@Kninnug 回答的帮助下,我设法编写了一段代码,可以为我生成所需的结果。
#include <stdio.h>
#include <locale.h>
#include <string.h>
#include <wchar.h>
#include <stdint.h>
int main(void)
{
size_t n = 0, x = 0;
setlocale(LC_CTYPE, "en_US.utf8");
mbstate_t state = {0};
char in[] = "!°水"; // or u8"zß水"
size_t in_sz = sizeof(in) / sizeof (*in);
printf("Processing %zu UTF-8 code units: [ ", in_sz);
for(n = 0; n < in_sz; ++n)
{
printf("%#x ", (unsigned char)in[n]);
}
puts("]");
wchar_t out[in_sz];
char* p_in = in, *end = in + in_sz;
wchar_t *p_out = out;
int rc = 0;
while((rc = mbrtowc(p_out, p_in, end - p_in, &state)) > 0)
{
p_in += rc;
p_out += 1;
}
size_t out_sz = p_out - out + 1;
printf("into %zu wchar_t units: [ ", out_sz);
for(x = 0; x < out_sz; ++x)
{
printf("%u ", (unsigned short)out[x]);
}
puts("]");
}
但是,当我在我的嵌入式设备上 运行 时,非 ASCII 字符会合并为一个 wchar,而不是像在我的计算机上那样合并为两个。
我可以使用 cp1252 的单字节编码(效果很好),但我想继续使用 unicode。
Achar
(signed
或unsigned
)在C1中是一个单字节。 (USHORT)test[0]
only cast only the first byte in test
, 但其中的字符在UTF-8编码中占2(可以用strlen
查看,它统计了前面的字节数第一个 0 字节)。
要获得正确的代码点,您需要解码整个 UTF-8 序列。您可以使用 mbrtowc
和相关函数执行此操作:
char* test;
test = "°";
int len = strlen(test);
wchar_t code = 0;
mbstate_t state = {0};
// convert up to len bytes in test, and put the result in code
// state is used when there are incomplete sequences: pass it to
// the next call to continue decoding
mbrtowc(&code, test, len, &state); // you should check the return value
// here the cast is needed, since a wchar_t is not (necessarily) a short
printf("test: %hu\n", (USHORT)code);
旁注:
如果 USHORT
是 16 位(通常是这种情况),它不足以覆盖整个 UTF-8 范围,它需要(至少)21 位。
当您获得了正确的代码点后,就不需要将其传递给绘图函数了。如果函数定义或原型可见,编译器可以自行转换值。
1 这个令人困惑的名字来自于 all the world's English 的时代,所有的 ASCII 代码点都可以放在一个字节中。因此,一个字符与一个字节相同。
这是我的第一个问题,如果我遗漏了重要规则,请随时批评或纠正我。
最近我的任务是将旧的 DOS C 代码移植到 Linux 平台。字体处理由位字体实现。我编写了一个函数,如果您将正确的 Unicode 值传递给它,它能够绘制选定的字形。
但是,如果我尝试将 char 转换为 USHORT(函数需要这种类型),当字符在 ASCII-table.
之外时,我会得到错误的值char* test;
test = "°";
printf("test: %hu\n",(USHORT)test[0]);
显示的数字(控制台)应该是 176 而不是 194。
如果你使用“!”将显示正确的值 33。 我通过设置 GCC 编译器标志
确保 char 是无符号的-unsigned-char
GCC 编译器默认使用 UTF-8 编码。我现在真的不知道问题出在哪里。
我是否需要向编译器添加另一个标志?
更新
在@Kninnug 回答的帮助下,我设法编写了一段代码,可以为我生成所需的结果。
#include <stdio.h>
#include <locale.h>
#include <string.h>
#include <wchar.h>
#include <stdint.h>
int main(void)
{
size_t n = 0, x = 0;
setlocale(LC_CTYPE, "en_US.utf8");
mbstate_t state = {0};
char in[] = "!°水"; // or u8"zß水"
size_t in_sz = sizeof(in) / sizeof (*in);
printf("Processing %zu UTF-8 code units: [ ", in_sz);
for(n = 0; n < in_sz; ++n)
{
printf("%#x ", (unsigned char)in[n]);
}
puts("]");
wchar_t out[in_sz];
char* p_in = in, *end = in + in_sz;
wchar_t *p_out = out;
int rc = 0;
while((rc = mbrtowc(p_out, p_in, end - p_in, &state)) > 0)
{
p_in += rc;
p_out += 1;
}
size_t out_sz = p_out - out + 1;
printf("into %zu wchar_t units: [ ", out_sz);
for(x = 0; x < out_sz; ++x)
{
printf("%u ", (unsigned short)out[x]);
}
puts("]");
}
但是,当我在我的嵌入式设备上 运行 时,非 ASCII 字符会合并为一个 wchar,而不是像在我的计算机上那样合并为两个。
我可以使用 cp1252 的单字节编码(效果很好),但我想继续使用 unicode。
Achar
(signed
或unsigned
)在C1中是一个单字节。 (USHORT)test[0]
only cast only the first byte in test
, 但其中的字符在UTF-8编码中占2(可以用strlen
查看,它统计了前面的字节数第一个 0 字节)。
要获得正确的代码点,您需要解码整个 UTF-8 序列。您可以使用 mbrtowc
和相关函数执行此操作:
char* test;
test = "°";
int len = strlen(test);
wchar_t code = 0;
mbstate_t state = {0};
// convert up to len bytes in test, and put the result in code
// state is used when there are incomplete sequences: pass it to
// the next call to continue decoding
mbrtowc(&code, test, len, &state); // you should check the return value
// here the cast is needed, since a wchar_t is not (necessarily) a short
printf("test: %hu\n", (USHORT)code);
旁注:
如果
USHORT
是 16 位(通常是这种情况),它不足以覆盖整个 UTF-8 范围,它需要(至少)21 位。当您获得了正确的代码点后,就不需要将其传递给绘图函数了。如果函数定义或原型可见,编译器可以自行转换值。
1 这个令人困惑的名字来自于 all the world's English 的时代,所有的 ASCII 代码点都可以放在一个字节中。因此,一个字符与一个字节相同。