写这篇文章遇到的的问题是c++操作正则的时候,遇到中文出现匹配失败。以及visual studio中中文乱码问题。当时这里卡住了项目进程,最后花费很多时间才解决。主要问题是中文编码编码问题。特记录分享,避免踩坑。
1. 三大编码由来和转换
计算机中所有数据存储都是01两个量表示,但是如何将01转换为我们日常使用的字符,这就依赖编码。众所周知,最早的计算机是美国人发明的,所以计算机最早表达的语言肯定是英文。英文字母只有26个。所以提出了单字节编码ASCII编码。用来解决字符在计算机中的存储表示问题。如大写的字符A编码为65(二进制01000001)一个字节就可以存储。
ASCII : 使用指定的 7 位或 8 位二进制数(单字节)组合来表示 128 或 256 种可能的字符。标准 ASCII 码也叫基础ASCII码,使用 7 位二进制数来表示所有的大写和小写字母,数字 0 到 9、标点符号, 以及在美式英语中使用的特殊控制字符。
其他国家,比如汉语也有字符编码的需求。但是ASCII采用单字节,表示的字符有限,无法满足其他语言编码的拓展。于是推出了多字节编码的Unicode编码来满足大多数国家的文字的编码。如:字符‘A’的编码为00000000 01000001,就是在ASCII中在高位加8个0。
Unicode:采用的是两个字节(16位二进制)来表示字符,这样能基本满足各种国家语言字符的需求。
虽然Unicode编码解决了世界文字编码大一统的问题,但Unicode只规定了符号的二进制编码,而并没有规定这个而二进制编码如何存储。通常一个中文字符需要两个字节来存储,特殊的字符可能需要三个四个字节存储。计算机无法知道几个字节表示一个字符,也就无法识别unicode码。如果规定每个unicode码用固定的多个字节来保存,那英文字符前面需要添加多个0,这是对存储是极大的浪费。在解决内存空间这件事上,从来难不倒抠门的码农。于是设计出了UTF8编码。
UTF-8: 是一种针对Unicode的可变长度的字符编码方式。它可以用一至四个字节对Unicode字符集中的所有有效编码点进行编码。
UTF8编码是一种变长的编码方式。其使用1-4字节来表示一个符号 。UTF8编码方式很简单,只有两个规则:
>1. 对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。
以A为例: ASCII: 01000001 --> Unicode: 00000000 01000001 --> UTF8: 01000001
- 对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。
以‘中’(4E2D)字为例:4E2D 在在第三行的范围内(0000 0800 - 0000 FFFF)所以‘中’编码需要三个字节。即格式是1110 xxxx 10xx xxxx 10xx xxxx。从最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。 UTF8: 100 1110 0010 1101 -> Unicode: 1110 0100 1011 1000 1010 1101
2. 三大编码在计算机中应用:
- 在计算机内存磁盘交互场景中:
在计算机内存中统一使用Unicode编码,进行字符处理,但是数据保存到磁盘文件中,先转换成UTF8以节约空间,然后呢存储。
内存读取磁盘文件中字符数据,也需要先将UTF8数据转换成Unicode编码,然后保存在内存中供计算机处理。
- web数据传输:
服务器中以Unicode编码,来保存数据,但是在向浏览器发送数据时候,会将字符转换成UTF8,发送给浏览器。以缩小通信数据。
3. char(string)和wchar_t (wstring)转换
关于这个问题的讨论起源于正则表达式。在中文匹配的时候,正则表达式无法匹配到中文符号。经过查阅资料发现,正则表达表达式是按照Unicode编码来匹配中文的。但是string中存储的是中文是UTF8编码,而非Unicode编码的字符导致无法匹配。要想正则表达是必须将中文字符集编码转换成Unicode编码。
window:转换
std::wstring Utf82Unicode(const std::string& utf8){
int unicodeLen = MultiByteTowideChar(CP_UTF8, 0, utf8.c_str(), -1, nullptr,0);
wchar_t* pUnicode = (wchar_t*)malloc(sizeof(wchar_t) * unicodeLen);
MultiByteTowideChar(CP_UTF8, 0, utf8.c_str(), -1, pUnicode, unicodeLen);
std::wstring ret str = pUnicode;
free(pUnicode);
return ret str;
}
std::string Unicode2Utf8(const std::wstring& wstr)
int ansiilen = WideCharToMultiByte(CP_UTF8, 0, wstr.c str(), -1, nullptr, 0, nullptr, nullptr);
char* pAscii = (char*)malloc(sizeof(char) * ansiiLen);
WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, pAssii, ansiilen, nullptr, nullptr);
std::string str = pAscii;
free(pAscii);
return str;
std::wstring Ascii2Unicode(const std::string& str){
int unicodeLen = MultiByteTowideChar(CP ACP, , str.c_str(), -1, nullptr,0);
wchar_t* pUnicode = (wchar_t*)malloc(sizeof(wchar_t) *unicodeLen);
MultiByteTowideChar(CP_ACP, 0, str.c_str(), -1, pUnicode, unicodeLen);
std::wstring str = pUnicode;
free(pUnicode):
return str;
}
std::string Unicode2Ascii(const std::wstring& wstr){
int ansiilen = WidecharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, nullptr, 0, nullptr, nullptr);
char* pAssii = (char*)malloc(sizeof(char) * ansiiLen);
WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, pAssii, ansiilen, nullptr, nullptr);
std::string str = pAssii;
free(pAssii);
return str;
}
// utf8和ascii 无法直接互转可通过unicode 转换实现。
linux:转换
std::wstring Utf82Unicode(const std::string& utf8){
wstring result;
const char * pUtf8Buff = utf8.c_str();
// 地域设置信息
char* old locale = setlocale(LC_CTYPE,"en_US.UTF8");
//将多字节字符串转换为宽字符串,确定宽字节长度
int unicode buff size = mbstowcs(NULL, pUtf8Buff, 0);
if(0 == unicode buff_size){
return result;
}
unicode_buff_size += 8; // 给更多额外的空间
result.resize(unicode_buff_size):
//将多字节字符串转换为宽字串
unicode_buff_size = mbstowcs((wchar t*)result.c_str(), putf8Buff, unicode_buff_size + 1):
setlocale(LC_CTYPE, old_locale);
return result:
}
std::string Unicode2Utf8(const std::wstring& wstr) {
string result;
const wchar t * pwBuff = wstr.c_str();//地域设置信息
char* old locale = setlocale(LC_CTYPE,"en_US.UTF8");
int Utf8Buffsize = wcstombs(NULL, pwBuf, 0);
if( == Utf8Buffsize) {
return result;
}
Utf8Buffsize += 8; // 给更多额外的空间result.resize(Utf8Buffsize);
Utf8Buffsize = wcstombs((char*)result.c_str(), pwBuff Utf8Buffsize + 1);
setlocale(LC_CTYPE, old_locale);
return result:
}