C-string to unicode

后台的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:

  1. 这个C库的代码:
    utf8.h (http://www.cprogramming.com/tutorial/utf8.h)
    utf8.c (http://www.cprogramming.com/tutorial/utf8.c)
  2. JSON and escaping characters



– EOF –

Categories: c
Tags: c,unicode,utf8