中文编码

gb2312 => gbk => gb18030

unicode在制定的时候没有考虑已有的编码, 所以和gb2312/gbk是不兼容的, 只能通过查表的方式进行转换.

详情参见: ​​笔记:ASCII, GB2312, GBK, Unicode, UTF8之间的区别和联系​

unicode

unicode的出现是为了解决不同地区之间的编码混乱的问题.

unicode对世界上绝大部分的文字进行整理和统一编码. 编码空间为​​U+0000到U+10FFFF​​.

unicode只是统一规定了哪个字符对应哪个编码, 并没有规定编码怎么存储. 实际实现的时候却可以有不同的方式, 常见的有: ​​utf-8​​/​​utf-16​​/​​utf-32​​/​​ucs2​​/​​ucs4​​.

​utf-8​​和​​utf-16​​是变长的编码方案.

​ucs2​​和​​ucs4​​是定长的编码方案.

​utf-32​​和​​ucs4​​是等价的.

utf8

utf8(8-bit Unicode Transformation Format)是一种变长的编码方案, 以8bit(1byte)作为最小编码单位, 一个unicode码可以编码成1至4个字节.

编码规则如下表:

Unicode符号范围(十六进制)

UTF-8编码方式(二进制)

0000 0000-0000 007F

0xxxxxxx

0000 0080-0000 07FF

110xxxxx 10xxxxxx

0000 0800-0000 FFFF

1110xxxx 10xxxxxx 10xxxxxx

0001 0000-0010 FFFF

11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

utf16

utf16(16-bit Unicode Transformation Format)也是一种变长的编码方案, 以16bit(2byte)作为最小编码单元, 一个unicode码可以编码为1至2个码元, 每个码元占2个字节.

编码规则如下:

Unicode码在​​0x0000-0xffff​​范围内的字符(也是绝大多数平时用到的字符)占一个码元, 并且码元的值和unicod码是相等的不需要额外转换.

对于超出​​0xffff​​范围的Unicode码才需要进行编码.

utf32

utf32直接用4个字节存储一个unicode码, 不需要额外的编码规则.

ucs2(2-byte Universal Character Set)

是一种定长编码方式, 只能表示​​0x0000-0xffff​​范围内的unicod码, 在这个范围内和utf16是等价的.

ucs4(4-byte Universal Character Set)

也是一种定长的编码方式, 用4个字节存一个字符, 所以可以存下所有的unicode码, 和utf32是等价的.

但是会存储空间.

BOM(Byte Order Mark)

码元超过1个字节的时候, 都会存在字节序的问题.

BOM(字节序标记)就是插入到UTF-8、UTF-16或UTF-32编码的Unicode文件开头的特殊标记, 用于标识文本编码及字节序.

编码

BOM

UTF-8

0xEF 0xBB 0xBF

UTF-16 BE

0xFE 0xFF

UTF-16 LE

0xFF 0xFE

UTF-32 BE

0x00 0x00 0xFE 0xFF

UTF-32 LE

0xFF 0xFE 0x00 0x00

c++对unicode的支持

参考: ​​C++11新特性--Unicode支持​

c++11引入了char16_t和char32_t类型来明确UTF-16和UTF-32编码方案对应的存储类型.

与之对应的STL库中多了相应的std::u16string和std::u32string.

char u8[] = u8"\u4f60\u597d\u554a";
char16_t u16[] = u"\u4f60\u597d\u554a";
char32_t u32[] = U"\u4f60\u597d\u554a";

//utf32 to utf16
std::u32string u32wstr(u32emoji);
std::wstring_convert<std::codecvt_utf16<char32_t , 0x10ffff, std::little_endian>, char32_t> utf16le_cvt;
std::string stru16bytes = utf16le_cvt.to_bytes(u32wstr);
char16_t* p16 = (char16_t*)(stru16bytes.c_str()); //观察内存可以看到大于2字节的emoji字符, 从u32转到u16, 做了相应的编码

//u16 to u8
//std::wstring_convert<std::codecvt_utf8<char16_t>, char16_t> utf8_ucs2_cvt;
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> utf8_ucs2_cvt;
std::string stru18emoji = utf8_ucs2_cvt.to_bytes(p16);
std::u16string stru16emoji = utf8_ucs2_cvt.from_bytes(stru18emoji);

//utf8 to u32
std::wstring_convert<std::codecvt_utf8<char32_t >, char32_t> u8_u32_convert;
std::string u32tou8 = u8_u32_convert.to_bytes(u32emoji); //u32 to u8
std::u32string stru32emoji = u8_u32_convert.from_bytes(stru18emoji);//u8 to u32

//uft8 to u16
std::wstring_convert<std::codecvt_utf8_utf16<char16_t >, char16_t> u8_u16_convert;
std::u16string stru16emoji2 = u8_u16_convert.from_bytes(stru18emoji);


linux下处理utf8编码

  • 查看文件编码:
$ file [filename]


  • 编码转换
$ iconv -f [from_encoding] -t [to_encoding] [inputfile] -o [outputfile]
$ iconv -f utf-8 -t unicode utf8file.txt > unicodefile.txt


#include <iconv.h>
// ...
iconv_t iconv_open(const char *tocode, const char *fromcode);
size_t iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
int iconv_close(iconv_t cd);