UTF8与UniCode之间的相互转换
这里不对两种编码进行官方的解释,如有不懂的可以自行百度。这里只对相互转换进行讨论。
UniCode | UTF8 |
---|---|
00000000 - 0000007F | 0xxxxxxx |
00000080 - 000007FF | 110xxxxx 10xxxxxx |
00000800 - 0000FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
00010000 - 001FFFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
此表是转换规则表,左边代表Unicode编码,右边代表utf8编码。将Unicode编码转换为二进后,根据它的范围填入UTF8对应的表中,不足的高位补0;
例子:UniCode转UTF8
李
Unicode:0000674E
UTF-8:E69D8E
- Unicode十六进制转二进制:0000674E->0110 0111 0100 1110
- 找出Unicode范围对应的UTF8编码:1110xxxx 10xxxxxx 10xxxxxx
- 将转换后的二进制从后往前代替UTF8编码中的X,不足高位 补0:11100110 10011101 10001110
- 转换好的二进制分别转成十六进制(按空格分割):E69D8E
UTF8转UniCode逆向操作就可以了,不再做描述
UniCode转UTF8代码
先说一下在编程中遇到的问题
- wchar_t在Linux中是占4个字节的,但是在window中只占2个字节,导致UTF8编码有时候到3个字节了高位自动丢失了,随后换成了unsigned int占4个字节
- 在参数传递中还是wchar_t类型,是因为Unicode编码是1-6个字节的,但是在转UTF8规则中没有超过2个字节的,就继续使用了
- 换成unsigned int达到了目的,但是不知道怎么输出出来,一直输出的是十进制,于是改变方案,使用vector类型,这样解决了unsigned int不好输出的问题,又解决了wchar_t只占2个字节丢失数据的问题
- 输出的时候需要修改CMD的编码格式,不然还是会乱码的
- 代码中使用了大量的位运算符,如果对这些运算符不了解,阅读改代码比较困难
#include <iostream>
#include <vector>
#include <cstdio>
std::vector<unsigned char> uniCodeToUTF8(const wchar_t& wc)
{
std::vector<unsigned char> utf8Str;
//00-7F范围内
if (wc <= 0X7F)
{
//添加到结果字符串中
utf8Str.push_back(static_cast<char>(wc));
}
//80-7FF之间
else if (wc <= 0X7FF)
{ //高位
utf8Str.push_back(static_cast<unsigned char>((wc >> 6) | 0XC0 & 0XDF));
//低位
utf8Str.push_back(static_cast<unsigned char>(wc & 0X3F | 0XC0 & 0XBF));
}
//800-FFFF之间
else if (wc <= 0XFFFF)
{
utf8Str.push_back(static_cast<unsigned char>((wc >> 12) | 0XE0 & 0XEF));
utf8Str.push_back(static_cast<unsigned char>((wc >> 6) & 0X3F | 0XC0 & 0XBF));
utf8Str.push_back(static_cast<unsigned char>(wc & 0X3F | 0XC0 & 0XBF));
}
//10000-10FFFF之间
else if (wc <= 0X10FFFF)
{
utf8Str.push_back(static_cast<unsigned char>((wc >> 18) | 0XF0 & 0XF7));
utf8Str.push_back(static_cast<unsigned char>((wc >> 12) & 0X3F | 0XC0 & 0XBF));
utf8Str.push_back(static_cast<unsigned char>((wc >> 6) & 0X3F | 0XC0 & 0XBF));
utf8Str.push_back(static_cast<unsigned char>(wc & 0X3F | 0XC0 & 0XBF));
}
return utf8Str;
}
int main()
{
wchar_t unicode = L'\u674E';
std::vector<unsigned char> str = uniCodeToUTF8(unicode);
str.push_back('\0');
printf("%s\n",str.data());
}
UTF8转UniCode代码
先说一下在编程中遇到的问题
- 可以看见我传入的是一个std::vector&,上面代码中写到wchar_t占用字节的问题,两边就都改成了unsigned int,但是这边并不存在输出,所以就没有修改
- std::wstring是一个宽字符串,每一个占用2个字节,上面也提到了Unicode与UTF转换规则,2个字节就已经够用了
- std::wcout 是专用与输出std::wstring和wchar_t的,但是不知道为什么输出不出来,这里建议写入文件再来观察;笔者是通过debug去观察的,转换没有问题就没有考虑输出了;主要是看转换的代码,重点并不是输出。
#include <iostream>
#include <vector>
std::wstring UTF8ToUnicode(const std::vector<unsigned int>& utf8)
{
std::wstring uniCodeStr;
for (unsigned int wc : utf8)
{
wchar_t temp = 0;
//判断范围 高位是否为11110
if (wc > 0XEFBFBF)
{
if ((wc >> 27) == 0X1E)
{
//满足条件,开始剥位
temp = temp | ((wc >> 24) & 0X07);
temp = (temp << 6) | ((wc >> 16) & 0X3F);
temp = (temp << 6) | ((wc >> 8) & 0X3F);
temp = (temp << 6) | (wc & 0x3F);
}
}
//高位是否为1110
else if (wc > 0XDFBF)
{
if ((wc >> 20) == 0x0E)
{
temp = temp | ((wc >> 16) & 0X0F);
temp = (temp << 6) | ((wc >> 8) & 0X3F);
temp = (temp << 6) | (wc & 0x3F);
}
}
//高位是110
else if (wc > 0X7F)
{
if ((wc >> 13) == 0X06)
{
temp = temp | ((wc >> 8) & 0X1F);
temp = (temp << 6) | (wc & 0x3F);
}
}
else
{
if ((wc >> 7) == 0X00)
{
temp = temp | (wc & 0X7F);
}
}
uniCodeStr.push_back(static_cast<wchar_t>(temp));
}
return uniCodeStr;
}
int main()
{
std::vector<unsigned int> utf8Str;
utf8Str.push_back(0XE69D8E);
std::wstring uniCodeStr = UTF8ToUnicode(utf8Str);
std::wcout << uniCodeStr << std::endl;
return 0;
}