使用C在终端中打印多字节字符

我一直在试验一个自定义字符串对象(struct),如下所示:

typedef struct { int encoding; int length; character * array; } EncodedString; 

我的想法是,通过指定编码,我可以制作一些使用该编码的函数来正确打印字符串,即ASCII或utf-8或utf-16等。(请原谅我的字符编码无知。)

现在,我正打算打印出一个(普通话)汉字:狗(0x72d7)。 我想也许是通过逐个字符打印它,它会正常工作,但显然不是。 它打印只是“r?” (分别为0x72和0xd7)。 那么如何修改这个程序以便它打印出角色?

 #include  typedef unsigned char character; typedef struct { int encoding; int length; character * array; } EncodedString; void printString(EncodedString str); int main(void) { character doginmandarin[] = {0x72U, 0xd7U}; EncodedString mystring = {0, sizeof doginmandarin, doginmandarin}; printString(mystring); printf("\n"); return 0; } void printString(EncodedString str) // <--- where I try to print the character { int i; for(i = 0; i < str.length; i++) { printf("%c", str.array[i]); } } 

理想情况下,我更喜欢如果我包含字符的数组只包含无符号字符,这意味着将构成字符狗的两个字节分开。 虽然它现在没有任何用途,但我们的想法是使用EncodedString结构的encoding字段来确定每个字符的字节数。

如何用最少量的黑客实现这一点?

数字Ox72d7是要打印的字符的Unicode代码点(抽象编号)。 当在内存中用两个字节0x72, 0xd7 ,它成为该字符的UCS-2代码,也恰好是它的UTF-16编码。 但是你的终端可能期望UTF-8编码的字符。 代码点Ox72d7的正确UTF-8编码是0xe7, 0x8b, 0x97

您可以修复代码以使用UTF-8编码字符,但这种编码对于内存表示非常不切实际,因为它会为不同的字符生成不同数量的字节。 这使得简单的字符串操作比如使第n个字符非常复杂。 相反,经常使用固定长度的表示。 例如,UCS-2每个字符总是使用两个字节。 然后,在打印字符串之前尽可能晚地完成到外部表示编码的转换。

编辑(来自评论)

UTF-8是一种棘手的编码。 从代码点到UTF-8字节的映射并不简单,涉及一些按位的mumbo-jumbo。 它是一种霍夫曼代码,不同的前缀表示字符占用的字节数。 此外,所有以下字节都以0b10开头,以便检测格式错误的UTF-8。 它在这里描述: http : //en.wikipedia.org/wiki/UTF-8#Description

为了快速找到我的post的三个字节,我只需在python控制台中输入: u"\u72d7".encode('UTF-8')

您应该查看与宽字符(wchar_t)和多字节字符串有关的c库函数。 linux上的c-library实现(或者据我所知的windows)与unicode兼容。 (如果你需要在你的微控制器板上使用它,你可能会运气不好)。 大多数处理utf-8编码和unicode的东西都已存在,所以你不需要自己动手。 以下是如何处理一个角色的示例:

 #include  #include  #include  int main () { /* * use an utf-8 compatible locale. */ setlocale (LC_ALL, "en_US.utf8"); const wchar_t dog = 0x72d7; /* * wchar_t strings can contain any character. Create one * string containing only the dog. */ wchar_t in[2] = { dog, 0 }; char out[100]; /* * convert to a multibyte string, returns the number of chars. */ size_t len = wcstombs (out, in, sizeof out); printf ("the character '%lc' is %zd bytes (string: '%s')\n", dog, len, out); } 

输出:

 $ ./a.out the character '狗' is 3 bytes (string: '狗')