一、字符集和编码(Charset & Encoding)¶
字符集(Charset):是一个系统支持的所有抽象字符的集合。字符是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。菜鸟教程:字符集和字符编码(Charset 和 Encoding) 字符编码就是将符号转换为计算机可以接受的数字系统的数,称为数字代码。
ASCII字符集:主要包括控制字符(回车键、退格、换行键等);可显示字符(英文大小写字符、阿拉伯数字和西文符号)。 ASCII编码:使用7位(bits)表示一个字符,共128字符;但是7位编码的字符集只能支持128个字符,为了表示更多的欧洲常用字符对ASCII进行了扩展(即EASCII),ASCII扩展字符集使用8位(bits)表示一个字符,共256字符。
1.1 中文编码¶
ASCII ⇒ GB2312 ⇒ GBK=CP936 ⇒ GB18030
GB2312或GB2312-80是中国国家标准简体中文字符集,它所收录的汉字已经覆盖中国大陆99.75%的使用频率。还把数学符号、罗马希腊的 字母、日文的假名们都编进去了。
GB2312编码:规定一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到 0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。
有时候显示为zh_CN.GB2312
GB 18030,全称:国家标准GB 18030-2005《信息技术 中文编码字符集》,是中华人民共和国现时最新的内码字集,是GB 18030-2000《信息技术 信息交换用汉字编码字符集 基本集的扩充》的修订版。与GB 2312-1980完全兼容,与GBK基本兼容,支持GB 13000及Unicode的全部统一汉字,共收录汉字70244个。GB18030 编码是一二四字节变长编码。
1.2 Unicode & UTF¶
在计算机科学领域中,Unicode(统一码、万国码、单一码、标准万国码)是业界的一种标准,它可以使电脑得以体现世界上数十种文字的系统。Unicode 是基于通用字符集(Universal Character Set)的标准来发展,并且同时也以书本的形式对外发表。
Unicode是字符集,也有对应的 Unicode 编码。Unicode 的码点(code point)范围是 U+0000 ~ U+10FFFF(共 21 位空间,分成 17 个平面)。最常用的基本多文种平面(BMP)落在 U+0000 ~ U+FFFF,正好可用 16 位表示,早期用 UCS-2 直接一一对应;但 UCS-2 已自 Unicode 2.0(1996)起被 UTF-16 取代,超出 BMP 的字符(如 emoji)需用代理对(surrogate pair)表示。在书写上,采用类如 U+???? 或 U+????? 的形式,其中每个“?”都是一个十六进制数。注意,Unicode 还有个 4 字节编码版本,亦即 UCS-4(与 UTF-32 在 Unicode 范围内等价),不在这里讨论。
,UTF-32/ UTF-16/ UTF-8是三种字符编码方案。 UTF-8 用 1 到 4 个字节编码 Unicode 字符(早期 RFC 2279 设计曾允许到 6 字节,但 RFC 3629 已将其限定为最多 4 字节,以与 UTF-16 的码点范围对齐)。 对于UTF-32和UTF-16编码方式还有一些其他不明显的缺点。不同的计算机系统会以不同的顺序保存字节。这意味着字符U+4E2D在UTF-16编码方式下可能被保存为4E 2D或者2D 4E,这取决于该系统使用的是大尾端(big-endian)还是小尾端(little-endian)。为了解决这个问题,多字节的Unicode编码方式定义了一个"字节顺序标记(Byte Order Mark)",它是一个特殊的非打印字符,你可以把它包含在文档的开头来指示你所使用的字节顺序。
UTF-8 是编码方式,en_US.UTF-8 和 zh_CN.UTF-8 是语言环境,也就是字符集.
C.utf8 = POSIX standards-compliant default locale. Only strict ASCII characters are valid, extended to allow the basic use of UTF-8
en_US.utf8 = American English UTF-8 locale.
1.3 编码转换¶
编码查询 千千秀字 - 字符集编码查询
convmv -f gb2312 -t utf-8 -notest -r music
请注意这里的"-notest"选项:如果不提供这个选项,该命令只会做一个转换的测试,并不会真正的转换。因为这个命令有一定的"破坏性",所以,当你用这个程序的时候,最好是先不用"-notest"这个选项来做一遍测试,根据程序运行输出的信息来确定是否有个别的文件需要手动进行调整。LinuxToy:convmv - 文件名编码转换
1.4 linux/git bash¶
查看支持的语言环境:locale -a
How to view the current locale setting?locale
How to set a user-specific locale?
#For sh, ksh:
$ LANG=C; export LANG
$ LC_ALL=C; export LC_ALL
#For csh:
$ setenv LANG C
$ setenv LC_ALL C
还可以通过$HOME/.local文件配置 man locale(1) — Linux manual page
$ mkdir -p $HOME/.locale
$ I18NPATH=./wrk/ localedef -f UTF-8 -i fi_SE $HOME/.locale/fi_SE.UTF-8
$ LOCPATH=$HOME/.locale LC_ALL=fi_SE.UTF-8 date
$ echo "export LOCPATH=\$HOME/.locale" >> $HOME/.bashrc
$ echo "export LANG=fi_SE.UTF-8" >> $HOME/.bashrc
如果使用git-bash, 则通过右键→Option→Text→Local→UTF-8修改编码。
参数说明:Linux 环境变量 LC_*、LANG、LC_ALL 区别详解
LC_CTYPE:用于字符分类和字符串处理,控制所有字符的处理方式,包括字符编码,字符是单字节还是多字节,如何打印等。是最重要的一个环境变量。LC_MONETARY:货币格式LC_NUMERIC:非货币的数字显示格式LC_TIME:时间和日期格式LC_MESSAGES:提示信息的语言。另外还有一个LANGUAGE参数,它与LC_MESSAGES相似,但如果该参数一旦设置,则LC_MESSAGES参数就会失效。LANGUAGE参数可同时设置多种语言信息,如LANGUAGE="zh_CN.GB18030:zh_CN.GB2312:zh_CN"。LANG:LC_*的默认值,是最低级别的设置,如果LC_*没有设置,则使用该值。类似于 LC_ALL。如果你的LANG环境变量是en_US.UTF-8,那么系统的菜单、程序的工具栏语言、输入法默认语言就都是英文的。LC_ALL:它是一个宏,如果该值设置了,则该值会覆盖所有LC_*的设置值。注意,LANG的值不受该宏影响。
1.5 代码中¶
在Linux中通过locale来设置程序运行的不同语言环境,locale由ANSI C提供支持。 打印语言环境:https://cplusplus.com/reference/clocale/setlocale/
std::cout << "The default locale is " << std::locale().name() << '\n'
<< "The user's locale is " << std::locale("").name() << std::endl;
printf ("Locale is: %s\n", setlocale(LC_ALL,NULL) );// 默认情况下使用 C locale(启动默认值)
printf ("Locale is: %s\n", setlocale(LC_ALL,"") );// 切到用户环境 locale,返回值字符串由实现/平台决定
...
实现定义行为
setlocale 返回的字符串格式(如 Windows 上的 "Chinese_China.936"、Linux 上的 "zh_CN.UTF-8")以及 std::locale::name() 的返回值,都属于 实现定义(implementation-defined)。C/C++ 标准只规定:返回非空表示成功、用 "C" 表示最小 locale、空字符串 "" 表示"使用环境默认"。具体平台接受的 locale 名字串(POSIX 风格 "zh_CN.UTF-8" 还是 Windows 的 "chs")和返回值拼写,由 C 运行库 / 标准库实现(glibc / MSVCRT / libstdc++ / libc++)决定,ISO 标准不管。
std::locale::global(std::locale("en_US.UTF-8"));在window ming64下有问题,暂未解决。
1.6 例子¶
wprintf 是 C 的标准库函数(<wchar.h> / <cwchar>),std::wcout 也是 C++ 标准库成员(<iostream> 提供的标准宽字符输出流)。C++ 中的 L"……" 是宽字符串字面量,类型为 const wchar_t[];但 wchar_t 的大小、以及 L"..." 里到底存的是不是 Unicode,由实现决定。
实现定义行为
wchar_t 的宽度(以及 L"..." 内容如何编码)属于 实现定义(implementation-defined) 行为,C++ 标准只要求 wchar_t 能放得下实现所支持的"扩展字符集"中的任一成员。具体取值不在 ISO 标准里,而是由各平台的 C++ ABI 决定:
- Windows / MSVC ABI:
sizeof(wchar_t) == 2,宽字符串通常按 UTF-16 存(含代理对)。 - Linux / macOS(Itanium C++ ABI):
sizeof(wchar_t) == 4,宽字符串通常按 UTF-32 存。 - ARM(AAPCS):
wchar_t为 4 字节,无专门的 wchar_t 类型(详见 AAPCS 文档)。
GCC 与 Clang 在 Linux/macOS 上都遵循 Itanium C++ ABI,所以这里 sizeof(wchar_t) 一致;MSVC 遵循自家的 MSVC ABI。
C 语言库中设置 locale 函数为 setlocale(), C++ 语言库中设置 std::locale 函数为 std::locale::global()。可认为 C 语言库和 C++ 库分别有不同的 locale 。setlocale() 函数仅设置 C 语言中的 locale,而 std::locale::global() 函数两者都会设置(自 C++11 起,若新 locale 有名字则同步调用 std::setlocale 设置 C locale)。std::wcout 输出乱码
C++ 中默认情况下 std::cout/std::wcout 与 printf/wprintf 是同步的(即 sync_with_stdio(true),默认值):标准保证二者按交错顺序一致可见,但性能开销更大。调用 std::ios::sync_with_stdio(false) 关闭同步后性能会改善,但不应再混用 iostream 与 C stdio 操作同一个文件——顺序不再保证。
未定义行为
std::cout(窄)与 std::wcout(宽)混用同一个 stdout 实际上踩到的是 C 标准 7.21.2 / 7.31.2 的"流方向(orientation)"规则:每个 FILE* 在第一次 I/O 后被固定为窄或宽方向,对已定向流做另一种操作属于 未定义行为(Undefined Behavior)。这一点 C++ 标准继承自 C,iostdio 的同步并不能消除它。结论:同一段程序里 cout 与 wcout 不要交替用。
//使用setlocale函数将本机的语言设置为中文简体
//LC_ALL表示设置所有的选项(包括金融货币、小数点,时间日期格式、语言字符串的使用习惯等),chs表示中文简体
setlocale(LC_ALL, "zh_CN.UTF-8");
wchar_t wt[] = L"中国伟大复兴梦"; //大写字母L告诉编译器这是宽字符字面量(每个字符按 wchar_t 宽度存,Windows=2 字节 / Linux&macOS=4 字节)
wcout << wt << endl; //使用wcout来代替cout输出宽字符
int main()
{
std::ios::sync_with_stdio(false);
std::locale::global( std::locale("") );
// 注意:global() 只改变此后新构造的 locale,不会改 wcout 已有的 locale
// 想让 wcout 真正用新 locale,还需 std::wcout.imbue(std::locale(""))
std::wstring wstr = L"你能输出中文?";
std::wcout << wstr << std::endl;
return 0;
}
二、问题¶
总的结论是:长字符操作放在 linux 下使用,windows 下环境暂时无法配置成功。
wchar_t 长字符串内容使用GBK:error:
converting to execution character set: Illegal byte sequence
原因:未设置-finput-charset=GBK,但即使增加了cc1plus.exe: error: failure to convert GBK to UTF-8,说明编译工具链不支持。
what(): locale::facet::_S_create_c_locale name not valid
说明不支持该字符集。
实现定义行为
这是 libstdc++(GCC) 抛出的异常文本,_S_create_c_locale 是 libstdc++ 内部符号。字符串的具体拼写属于 标准库实现相关——libc++/MSVC STL 抛出的是另一种 std::runtime_error 文本(如 MSVC 的 "bad locale name")。C++ 标准只规定 std::locale 构造失败时抛 std::runtime_error,不规定 what() 文本内容。
CSDN:locale::facet::_S_create_c_locale name not valid 解决办法
Cygwin 的 libstdc++ 中的 locale 实现 CSDN:Cygwin 下 libstdc++ locale 实现笔记 测试版本:g++ 4.3.4,bash 3.2.49,cygwin1.dll 1.7.1,libstdc++6 4.3.4-3 构造 locale 对象时,C.UTF-8,zh_CN.UTF-8,en_US.UTF-8 这些 locale 名字都无效,都会抛出runtime_error 异常,就连使用环境 locale 的空字符串参数 "" 都无效。只有一个 locale 名是有效的,就是最小的 C locale,即程序初始化时的默认locale。另外,Cygwin bash 启动后默认的 locale 为 C.UTF-8,可用查看 LANG 环境变量得知。
cannot execute binary file
- 非root用户或者无执行权限
- 编译环境不同(程序由其他操作环境复制过来)对于第一种情况,采用增加执行权限即可chmod +x program对于第二种情况,建议将该程序二进制包拷贝过来,重新编译程序。
- 硬件平台与软件不一致 , 例如: 32位系统,下载了个64位的软件,结果就无法执行 如果使用 file 命令检查的结果是 data, 而不是可执行文件, 那么在这个系统平台上不能直接运行这个文件 CSDN:cannot execute binary file 报错排查
三、std::cout¶
设置初始格式:std::dec, std::hex, std::oct