转载自:c++程序的编码与字符集的转换
一、程序相关的编码
-
程序源文件编码
程序源文件编码是指保存程序源文件内容所使用的编码方案,该编码方案可在保存文件的时候自定义。
通常在简体中文windows环境下,各种编辑器(包括visual studio)新建文件缺省编码都是
GB18030。所以不特别指定的话,在windows环境下,c++源文件的编码通常为
GB18030(GB18030兼容GBK);
在linux环境下,默认的为UTF-8编码。 -
c++程序内码
源程序编译后,c++中的字符串常量变成一串字节存放在可执行文件中,内码指的是在可执行文件中,字符串以什么编码进行存放。
这里的字符串常量指的是
窄字符(char)而不是宽字符(wchar_t)。
宽字符通常都是以Unicode(VC使用UTF-16BE,gcc使用UTF-32BE)存放。通常简体中文版的VC使用内码为
GB18030,而gcc使用内码缺省为UTF-8,但可以通过-fexec-charset参数进行修改。
(可以通过在程序中打印字符串中每个字节的16进制形式来判断程序使用的内码)。 -
运行环境编码
运行环境编码指的是,执行程序时,操作系统或终端所使用的编码。程序中输出的字符最终要转换为运行环境编码才能显示,否则就会出现乱码。
常用的简体中文版的windows环境编码是
GB18030,linux下最常用的环境编码是UTF-8。 -
三种编码之间的关系
程序源文件【源文件编码】→(编译器编译)→目标文件【程序内码】→(运行后输出信息)→输出【运行环境编码】
二、已知的问题
windows下,wchar_t的c++程序内码,VC默认使用UTF-16BE,所以wchar_t可能不支持生僻字。可以使用utf-8格式的char字符。
linux下,wchar_t的c++程序内码,默认使用UTF-32BE,所以生僻字应该没有这样的问题。
c++新标准里新增的char32_t,在windows下并未完全实现可用。标准输入输出流无法使用。
三、指定编译编码
| 功能 | g++编译器 | vs编译器 |
|---|---|---|
| 指定c++程序内码 | -fexec-charset=GBK | #pragma execution_character_set("utf-8") |
| 指定源文件编码 | -finput-charset=UTF-8 | |
| 指定c++程序宽字符(wchar_t)内码 | -fwide-exec-charset=UTF-16 | |
| 指定运行环境utf-8编码 | system("chcp 65001"); |
四、C++中的字符类型
| 字符类型 | 字符文本 | 字符串类型 | 字符串文本 | 编码 | 范围 |
|---|---|---|---|---|---|
| char | 'a' | string | "abc" | GBK或 UTF-8 | Windows 默认 GBKLinux 默认 UTF-8 |
| wchar_t | L'a' | wstring | L"abc" | UTF-16BE或 UTF-32BE | Windows 默认 UTF-16BELinux 默认 UTF-32BE |
| char8_t | u8'a' | u8string | u8"abc" | UTF-8 | UTF-8 |
| char16_t | u'a' | u16string | u"abc" | UTF-16 | BMP平面字符(即代码点 U+0000 到 U+FFFF 范围内的字符) |
| char32_t | U'a' | u32string | U"abc" | UTF-32 | UTF-32 |
(char8_t 是 C++20 中的新增功能,需要 /std:c++20 或 /std:c++latest 编译器选项)
五、字符集转换
- Windows平台
MFC程序中,如果从文件读入Utf-8字符集的字符在CString中,可以使用MultiByteToWideChar函数,转成Unicode字符集。
反之,Unicode字符集的CString,可以使用WideCharToMultiByte函数,转成GBK字符集或utf-8字符集的char[]或string。
void Utf8ToUnicode(CString& str)
{
// str是用CStdioFile类的ReadString()读取的Utf-8格式的文件,存入CString中
if (str.GetLength() == 0)
{
return;
}
PSTR szBuf = new CHAR[str.GetLength() + 1]; // 注意“+1”,char[]字符数组要求结束符,而CString中没有'\0'
memset(szBuf, '\0', str.GetLength() + 1);
// 将 WCHAR 转换成 CHAR
for (int i = 0; i < str.GetLength(); ++i)
{
szBuf[i] = (CHAR)str.GetAt(i); // 逐字节读取CString中的数据,存入char[]中
}
// 根据保存着utf8的char[],计算需要的宽字符字节数
INT nLen = MultiByteToWideChar(CP_UTF8, 0, szBuf, -1, NULL, 0);
LPWSTR ptch = new WCHAR[nLen];
// utf8 => unicode
MultiByteToWideChar(CP_UTF8, 0, szBuf, -1, ptch, nLen);
// 将转换后的unicode交给str
str = ptch;
// 回收空间
if (NULL != ptch)
delete[] ptch;
ptch = NULL;
if (NULL != szBuf)
delete[] szBuf;
szBuf = NULL;
}
LPSTR UnicodeToUtf8(CString& str, INT& nCount)
{
// CP_ACP 是 GBK,CP_UTF 是 Utf-8
nCount = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL); // 获得需要的宽字符字节数
LPSTR pChar = new CHAR[nCount];
WideCharToMultiByte(CP_UTF8, 0, str, -1, pChar, nCount, NULL, NULL); // utf8 => unicode
return pChar;
}
wstring是Unicode字符集,可以使用WideCharToMultiByte转换成gbk字符集的string或utf-8字符集的string。
#include <iostream>
#include <windows.h>
int main()
{
std::wstring pwszUnicode = L"你好𣍐";
int iSize;
char* pszMultiByte;
// CP_ACP 是 GBK,CP_UTF 是 Utf-8
iSize = WideCharToMultiByte(CP_UTF8, 0, pwszUnicode.c_str(), -1, NULL, 0, NULL, NULL);
pszMultiByte = new char[iSize + 1];
WideCharToMultiByte(CP_UTF8, 0, pwszUnicode.c_str(), -1, pszMultiByte, iSize, NULL, NULL);
std::string pszUtf8(pszMultiByte);
system("chcp 65001");
std::cout << pszUtf8 << std::endl;
delete[] pszMultiByte;
pszMultiByte = NULL;
return 0;
}
- Linux平台
Linux平台下可以使用 iconv() 函数
// https://www.cnblogs.com/huojing/articles/16291647.html
太复杂,略...
- 通用方式
使用 wstring_convert 转换(注意:c++17已弃用<codecvt>)
#include <iostream>
#include <locale>
#include <codecvt>
// vs编译器需要这一行
#pragma execution_character_set("utf-8")
std::string u32string_to_string(const std::u32string& str)
{
// 虽然在linux平台下此处使用codecvt_utf8_utf16和codecvt_utf8都能正常转换,但是还是不建议用codecvt_utf8_utf16
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> converter;
return converter.to_bytes(str);
}
std::u32string string_to_u32string(const std::string& str)
{
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> converter;
return converter.from_bytes(str);
}
std::string u16string_to_string(const std::u16string& str)
{
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter;
return converter.to_bytes(str);
}
std::string wstring_to_string(const std::wstring& str)
{
// 虽然在linux平台下此处使用codecvt_utf8_utf16和codecvt_utf8都能正常转换,但是还是不建议用codecvt_utf8_utf16
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
return converter.to_bytes(str);
}
std::wstring string_to_wstring(const std::string& str)
{
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
return converter.from_bytes(str);
}
int main()
{
// windows平台需要这一行
system("chcp 65001");
std::u32string u32str = U"中國人𣍐";
std::cout << u32string_to_string(u32str) << std::endl;
std::string str = "中國人𣍐";
std::cout << u32string_to_string(string_to_u32string(str)) << std::endl;
std::u16string u16str = u"中國人𣍐";
std::cout << u16string_to_string(u16str) << std::endl;
std::wstring wstr = L"中國人𣍐";
std::cout << wstring_to_string(wstr) << std::endl;
std::string str2 = "中國人𣍐";
std::cout << wstring_to_string(string_to_wstring(str2)) << std::endl;
}
本文详细讲述了C++程序中的编码概念,包括源文件编码、程序内码、运行环境编码,以及在Windows和Linux平台下不同字符类型如char、wchar_t、char32_t的处理,以及如何通过指定编译选项和函数进行字符集转换,如MFC中的MultiByteToWideChar和WideCharToMultiByte,以及Linux下的iconv和wstring_convert方法。
2万+

被折叠的 条评论
为什么被折叠?



