后台的C程序通过socket发给前台的消息包含了中文,前台接收后出现了乱码,有人提出让我把消息编码成unicode格式(\uxxxx),虽然参考了一个C库做了实现,但当时的思路并不是很清晰,回来做下归纳。
包含中文字符的消息,记为msg,假若msg包含中文、英文和数字:msg = “abc我是def中文123字符”,于是需要把msg编码成unicode格式,编码后的内容保存到C的char数组里,记为unicode_str,则最终想要得到的unicode_str = “abc\u6211\u662Fdef\u4E2D\u6587123\u5B57\u7B26”。
在c代码里,先用宽字符类型wchar_t保存msg,这个宽字符的长度可以用函数wcslen来计算,然后遍历每个宽字符,把宽字符转换成unicode格式。对每个宽字符的转换,参考了Unicode in C and C++: What You Can Do About It Today这里提供的一个函数: u8_escape_wchar。
int u8_escape_wchar(char *buf, int sz, uint32_t ch)
{
if (ch == L'\n')
return snprintf(buf, sz, "\\n");
else if (ch == L'\t')
return snprintf(buf, sz, "\\t");
else if (ch == L'\r')
return snprintf(buf, sz, "\\r");
else if (ch == L'\b')
return snprintf(buf, sz, "\\b");
else if (ch == L'\f')
return snprintf(buf, sz, "\\f");
else if (ch == L'\v')
return snprintf(buf, sz, "\\v");
else if (ch == L'\a')
return snprintf(buf, sz, "\\a");
else if (ch == L'\\')
return snprintf(buf, sz, "\\\\");
else if (ch < 32 || ch == 0x7f)
return snprintf(buf, sz, "\\x%hhX", (unsigned char)ch);
else if (ch > 0xFFFF)
return snprintf(buf, sz, "\\U%.8X", (uint32_t)ch);
else if (ch >= 0x80 && ch <= 0xFFFF)
return snprintf(buf, sz, "\\u%.4hX", (unsigned short)ch);
return snprintf(buf, sz, "%c", (char)ch);
}
每个宽字符先转换为unsigned int,也就是wchar_t到unsigned int。 demo大概如下:
int main()
{
setlocale(LC_ALL, "zh_CN.UTF-8");
wchar_t *input = L"abc我是def中文123字符";
int len = wcslen(input);
wchar_t *p = input;
char buf[2048] = {0x0};
char *pbuf = buf;
int ret;
int i; for (i = 0; i < len; i++)
{
ret = u8_escape_wchar(pbuf, sizeof(buf), (uint32_t)*p);
pbuf += ret;
p++;
}
printf("buf[%s]\n", buf);
setlocale(LC_ALL, "C");
return 0;
}
demo的输出为: abc\u6211\u662Fdef\u4E2D\u6587123\u5B57\u7B26
而通过gdb查看buf的内容,可以看到每个中文字符unicode前会多一个转义的’\’:
(gdb) p buf
$1 = "abc\\u6211\\u662Fdef\\u4E2D\\u6587123\\u5B57\\u7B26", '\0' <repeats 2002 times>
可以看到,对于英文字符和数字,并没有做转换。
前台的js拿到unicode之后的内容”abc\u6211\u662Fdef\u4E2D\u6587123\u5B57\u7B26”,可以简单转换回中文字符,js的转换思路,先用JSON.stringify把源内容序列化,再用正则替换unicode码:
function JSON_stringify(s, emit_unicode)
{
var json = JSON.stringify(s);
return emit_unicode ? json : json.replace(/[\u007f-\uffff]/g,
function(c) {
return '\\u'+('0000'+c.charCodeAt(0).toString(16)).slice(-4);
}
);
}
var s = "abc\u6211\u662Fdef\u4E2D\u6587123\u5B57\u7B26";
JSON_stringify(s, true);
输出为:abc我是def中文123字符
当然,上面的C库也提供了unicode到C-string的转换,转换的函数为:
int u8_unescape(char *buf, int sz, char *src);
第3个参数是要转换的unicode码,例如:
u8_unescape(mybuf, 256, "hello\\u220e")
ref:
- 这个C库的代码:
utf8.h (http://www.cprogramming.com/tutorial/utf8.h)
utf8.c (http://www.cprogramming.com/tutorial/utf8.c) - JSON and escaping characters
– EOF –