c++字符串的编码?

c++ 字符串加载到内存里面是什么编码格式的?

win7中文系统下,控制台默认是GBK编码的,用GBK格式保存的源文件,中文字符串在vs2010下编译输出到控制台会正常输出

但是vs2010里面采用utf-8无BOM的源文件 输出中文字符串到终端就出现乱码了

所以 是不是c++把字符串的值加载到内存中时 是按照cpp文件的编码保存的? 也就是说utf-8编码的cpp文件,编译后字符串加载到内存时是utf-8编码的?

 

这个问题想要说清楚还是挺复杂的,题主可以参考 New Options for Managing Character Sets in the Microsoft C/C++ Compiler 这篇文章。具体来说,源代码文件中的字符串常量能否正确的显示在控制台窗口中与以下几个因素有关:

  • 源代码文件(.cpp)保存时所使用的字符集(下文用 C1 代替)

  • 编译器在读取源代码文件时所使用的内部字符集(source character set)(下文用 C2 代替)

  • 编译器在生编译时所使用的字符集(execution character set)(下文用 C3 代替)

  • 运行可执行程序的控制台窗口所使用的字符集(下文用 C4 代替)

第一,“源代码文件保存时所使用的字符集”决定了源代码文件中的抽象字符保存到硬盘中是什么样的字节。这里所说的抽象字符,就是指人所能识别的文字(如“张”)。而字节则是由抽象字符按照字符集的规定映射的,不同的字符集映射的结果不一样(如“张”在 UTF-8 下映射的字节是E5 BC A0三个字节,而在 GBK 下映射的字节是D5 C5两个字节)。比如下面一行代码:

char *s = "张三";

其中的字符串常量"张三"在使用不同的字符集(C1)保存时,映射的字节是不同的。

第二,“编译器在读取源代码文件时所使用的内部字符集”决定了编译器如何把读入的源代码文件字节流进行转换。我所谓的转换就是指把字节流从一种编码转到另一种编码。举例来说,对于抽象字符“张”,如果采用 GBK 编码,映射的字节流就是D5 C5,而转换到 UTF-8 编码,就是把这个字节流转换成E5 BC A0

这个字符集(C2)是由编译器决定的,标准中并未规定。不同的编译器可能采用不同的内部字符集,同样的编译器不同版本也可能采用不同的内部字符集。在 Visual C++ 的某些版本中,内部字符集是 UTF-8,编译器会尝试判断源文件所使用的字符集(C1),并将其转换成内部字符集(C2),如果编译器不能判断出文件所使用的字符集,则会默认其(C1)为当前操作系统的代码页(default code page)这里如果判断错误,就会造成乱码或编译出错)。

比如:源文件如果是 UTF-8 with BOM,则能够正确的被 Visual C++ 识别,其中的抽象字符“张”映射的字节流E5 BC A0就会被正确的转换成E5 BC A0(没变)。而源文件如果是 UTF-8 without BOM,则不能正确的被 Visual C++ 识别,编译器会采用当前的代码页来进行转换,其中的抽象字符“张”映射的字节流E5 BC A0就会被当作 GBK 编码,错误的转换成其它字节流E5 AF AE ...(寮)。

第三,“编译器在编译时所使用的字符集”决定了编译器如何把源代码中使用内部字符集(C2)编码的字符/字符串常量转还到编译时所使用的字符集(C3)。这个字符集(C3)也是由编译器决定的,标准中并未规定。C++ 中的字符常量和字符串常量有不同的类型,它们对应于不同的 C3。

在 Visual C++ 中,参考 String and Character Literals 和前文提到的博客,可以推知不同类型的字符/字符串常量对应的 C3:

// Character literals
auto c0 =   'A'; // char, encoded as default code page
auto c1 = u8'A'; // char, encoded as UTF-8
auto c2 =  L'A'; // wchar_t, encoded as UTF-16LE
auto c3 =  u'A'; // char16_t, encoded as UTF-16LE
auto c4 =  U'A'; // char32_t, encoded as UTF-32LE

// String literals
auto s0 =   "hello"; // const char*, encoded as default code page
auto s1 = u8"hello"; // const char*, encoded as UTF-8
auto s2 =  L"hello"; // const wchar_t*, encoded as UTF-16LE
auto s3 =  u"hello"; // const char16_t*, encoded as UTF-16LE
auto s4 =  U"hello"; // const char32_t*, encoded as UTF-32LE

编译器根据字符串常量的类型把其从 C2 转换到 C3(前面如果判断错误,这里就会继续保留错误)。

比如:auto s1 = "张";,抽象字符“张”在 C2 中(UTF-8)映射的字节流E5 BC A0就会被转换成在 C3 中(CP936,GBK)映射的字节流D5 C5

auto s2 = u8"张";,抽象字符“张”在 C2 中(UTF-8)映射的字节流E5 BC A0就会被转换成在 C3 中(UTF-8)映射的字节流E5 BC A0(不变)。

第四,“运行可执行程序的控制台窗口所使用的字符集”决定了如何把编译好的可执行程序中的字节流转换成抽象字符显示在控制台中。比如,在上一步中的s1映射的字节流就会通过 C4(CP 936)映射回抽象字符“张”,在我们看来就是正确的。而上一步中的s2映射的字节流就会通过 C4(CP 936)映射回抽象字符“寮”,在我们看来就是乱码。

以上,就是我理解的 C++ 中字符/字符串的编码处理方式,如果有误还请指出:-)

题主可以尝试在 Visual C++ 中把以下代码分别保存成 CP936、UTF-8 with BOM、UTF-8 without BOM 的格式,看看输出结果是什么。

#include <iostream>
#include <fstream>
using namespace std;

int main() {
  char *s1 = u8"张";
  char *s2 = "张";
  cout << "s1 " << sizeof(s1) << " " << strlen(s1) << " -> " << s1 << endl;  // Error in console
  cout << "s2 " << sizeof(s2) << " " << strlen(s2) << " -> " << s2 << endl;  // OK in console

  ofstream os("s1.txt");
  if (os.is_open()) {
    os << "s1 " << sizeof(s1) << " " << strlen(s1) << " -> " << s1 << endl;  
    os.close();
  }
  ofstream os2("s2.txt");
  if (os2.is_open()) {
    os2 << "s2 " << sizeof(s2) << " " << strlen(s2) << " -> " << s2 << endl;  
    os2.close();
  }
  ofstream os3("s3.txt");
  if (os3.is_open()) {
    os3 << "s1 " << sizeof(s1) << " " << strlen(s1) << " -> " << s1 << endl;
    os3 << "s2 " << sizeof(s2) << " " << strlen(s2) << " -> " << s2 << endl;
    os3.close();
  }

  cin.get();
  return 0;
}

输出的三个文件中,前两个文件s1.txts2.txt都能够被正常的文本编辑器猜出其编码格式,从而正确显示内容,但是第三个文件s3.txt会显示成部分或全部乱码,因为其中既包含了 UTF-8 编码的字节流又包含了 GBK 编码的字节流,所以文本编辑器就不知道该用什么编码来把字节流映射回抽象文本了。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值