编码真的很重要吗? (例如,字符串存储,打印?)

时间:2010-10-30 14:38:52

标签: c++ character-encoding

对处理字符串存储(如果它关心)和打印时系统正在使用的编码感到好奇。

问题1:如果我在std :: string中存储一个字节的字符串或在std :: wstring中存储两个字节的字符串,那么基础整数值会根据当前使用的编码而有所不同吗? (我记得Bjarne说编码是char和整数之间的映射,所以char应该作为整数存储在内存中,不同的编码不一定有相同的映射)

问题2:如果是正面的,std :: string和std :: wstring必须具备编码本身的知识(虽然另一个人告诉我这不是真的)?否则,如何将char转换为正确的整数并存储它们?系统如何知道编码?

问题3:一个特定系统中的默认编码是什么,以及如何更改它(它是所谓的“语言环境”)?我想同样的机制很重要吗?

问题4:如果我用std :: cout在屏幕上打印一个字符串,它的编码是相同的吗?

4 个答案:

答案 0 :(得分:4)

  

(我记得Bjarne说过   encoding是char之间的映射   和整数(s)所以char应该是   作为整数存储在内存中)

不完全。确保你理解一个重要的区别。

  • 字符 text 的最小单位。字母,数字,标点符号,符号,空格等
  • 字节内存的最小单位。在绝大多数计算机上,这是8位。

编码正在将字符序列转换为字节序列。 解码正在将字节序列转换为字符序列。

对于C和C ++程序员来说,令人困惑的是 char表示字节,而不是字符!字节类型的名称char是来自Unicode之前的遗留物每个人(东亚人除外)使用单字节编码的日子。但是现在,我们有Unicode,其编码方案每个字符最多4个字节。

  

问题1:如果我存储一个字节的字符串   在std :: string或两个字节的字符串中   std :: wstring,将底层   整数值取决于编码   目前正在使用?

是的,它会的。假设你有std::string euro = "€";那么:

  • 使用windows-1252编码,字符串将被编码为字节0x80。
  • 使用ISO-8859-15 encoding,字符串将被编码为字节0xA4。
  • 使用UTF-8编码,字符串将被编码为三个字节0xE2,0x82,0xAC。
  

问题3:什么是默认值   在一个特定的系统中编码,和   如何改变它(它是所谓的   “区域设置”)?

取决于平台。在Unix上,可以将编码指定为LANG环境变量的一部分。

~$ echo $LANG
en_US.utf8

Windows有一个GetACP函数来获取“ANSI”代码页码。

  

问题4:如果我打印一个字符串怎么办?   用std :: cout到屏幕,是吗   相同的编码?

不一定。在Windows上,命令行使用“OEM”代码页,这通常与其他地方使用的“ANSI”代码页不同。

答案 1 :(得分:1)

编码和解码本质上是相同的过程,即它们都将一个整数序列变换为另一个整数序列。

编码和解码之间的区别在于概念层面。当您“解码”某个字符时,您将以已知编码(“字符串”)编码的整数序列转换为系统特定的整数序列(“text”)。当你“编码”时,你正在将系统特定的整数序列(“text”)转换为以特定编码编码的整数序列(“string”)

这种差异是概念性的,而不是物理的,记忆仍然将解码的“文本”保存为“字符串”;然而,由于特定系统始终以特定编码表示“文本”,因此文本转换不需要处理实际系统编码的特定性,并且可以安全地假设能够处理一系列概念性“字符”而不是“字节”。

然而,通常,用于“文本”的编码使用具有易于使用的属性的编码(例如,固定长度字符,字符和字节序列之间的简单一对一映射等);而编码的“字符串”使用有效的编码进行编码(例如,可变长度字符,依赖于上下文的编码等)

Joel On Software对此进行了撰写:http://www.joelonsoftware.com/articles/Unicode.html

这个也很好:http://www.jerf.org/programming/encoding.html

答案 2 :(得分:0)

  

问题1:如果我存储一个字节的字符串   在std :: string或两个字节的字符串中   std :: wstring,将底层   整数值因不同而异   目前正在使用的编码? (我记得   Bjarne说编码是   char和整数之间的映射   char应存储为整数   记忆,不同的编码不   必须有相同的映射)

你有点想着这个倒退。不同的编码基础整数解释为不同的字符(或者字符的一部分,如果我们讨论的是多字节字符集),具体取决于编码。

  

问题2:如果是肯定的,std :: string和std :: wstring必须有   编码的知识   他们自己(虽然另一个人说   我这不是真的)?否则怎么样   是否能够将char转换为   正确的整数并存储它们?怎么样   系统知道编码吗?

std::stringstd::wstring都是完全编码不可知的。就C ++而言,它们只是分别存储char个对象和wchar_t个对象的数组。唯一的要求是char是一个字节,wchar_t是一些实现定义的宽度。 (通常在Windows上为2个字节,在Linux / UNIX上为4个字节)

  

问题3:什么是默认值   在一个特定的系统中编码,和   如何改变它(它是所谓的   “区域设置”)?

这取决于平台。 ISO C ++仅讨论全局语言环境对象std::locale(),它通常是指您当前系统特定的设置。

  

问题4:如果我打印一个字符串怎么办?   用std :: cout到屏幕,是吗   相同的编码?

通常,如果通过stdout输出到屏幕,则会根据系统当前的区域设置来解释和呈现您显示的字符。

答案 3 :(得分:0)

任何使用编码的人都应该阅读这篇Joel on Software文章:The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)。当我开始使用编码时,我发现它很有用。

  

问题1:如果我在std :: string中存储一个字节的字符串或在std :: wstring中存储两个字节的字符串,那么基础整数值会根据当前使用的编码而有所不同吗?

C / C ++程序员习惯将字符视为字节,因为几乎每个人都开始使用ascii character set,将整数0-255映射到符号,如字母和阿拉伯数字。事实上,C char数据类型实际上是一个字节,这无关紧要。

std::string类将数据存储为8位整数,std::wstring将数据存储为16位整数。这两个类都不包含任何编码概念。您可以使用任何8位编码,例如ASCIIUTF-8Latin-1Windows-1252std::string,以及任何8位或16位编码,例如UTF-16std::wstring

std::stringstd::wstring中存储的数据必须始终由某些编码解释。当您与操作系统交互时,这通常会发挥作用:从文件,流中读取或写入数据,或者进行与字符串交互的OS API调用。

所以要回答你的问题,如果你在std::stringstd::wstring中存储相同的字节,内存将包含相同的值({{1除外)将包含一个空字节),但该字节的解释将取决于使用的编码。

如果在每个字符串中存储相同的字符,则字节可能会有所不同,这取决于编码。例如,Euro symbol(€)可能使用UTF-8编码存储在wstring中,该编码对应于字节0xE2 0x82 0xAC。在std::string中,它可能使用UTF-16编码存储,即0x20AC字节。

  

问题3:一个特定系统中的默认编码是什么,以及如何更改它(它是所谓的“语言环境”)?我想同样的机制很重要吗?

是的,语言环境决定了OS如何解释其API边界的字符串。 Locale定义的不仅仅是编码。它们还包括有关如何格式化金钱,日期,时间和其他内容的信息。在Linux或OS X上,您可以使用终端中的std::wstring命令查看当前的语言环境:

locale

所以在这种情况下,我的语言环境是加拿大英语。每个语言环境都定义了用于解释字符串的编码。在这种情况下,语言环境名称清楚地表明它使用的是UTF-8编码,但您可以运行mch@bohr:/$ locale LANG=en_CA.UTF-8 LC_CTYPE="en_CA.UTF-8" LC_NUMERIC="en_CA.UTF-8" LC_TIME="en_CA.UTF-8" LC_COLLATE="en_CA.UTF-8" LC_MONETARY="en_CA.UTF-8" LC_MESSAGES="en_CA.UTF-8" LC_PAPER="en_CA.UTF-8" LC_NAME="en_CA.UTF-8" LC_ADDRESS="en_CA.UTF-8" LC_TELEPHONE="en_CA.UTF-8" LC_MEASUREMENT="en_CA.UTF-8" LC_IDENTIFICATION="en_CA.UTF-8" LC_ALL= 以查看有关当前编码的更多信息:

locale -ck LC_CTYPE

如果要使用编码测试程序,可以将LC_ALL环境变量设置为要使用的语言环境。您还可以使用mch@bohr:/$ locale -ck LC_CTYPE LC_CTYPE ctype-class-names="upper";"lower";"alpha";"digit";"xdigit";"space";"print";"graph";"blank";"cntrl";"punct";"alnum";"combining";"combining_level3" ctype-map-names="toupper";"tolower";"totitle" ctype-width=16 ctype-mb-cur-max=6 charmap="UTF-8" ... output snipped ... 更改区域设置。永久更改区域设置取决于您的分发。

在Windows上,大多数API函数都采用窄格式和宽格式。例如,setlocale包含GetCurrentDirectoryW(Unicode)和GetCurrentDirectoryA(ANSI)变体。在此上下文中,Unicode表示UTF-16。

我不太了解Windows如何设置语言环境,除了尝试语言控制面板。

  

问题4:如果我用std :: cout在屏幕上打印一个字符串,它的编码是相同的吗?

当您将字符串打印到[GetCurrentDirectory][9]时,操作系统将在语言环境的编码集中解释该字符串。如果您的字符串是UTF-8编码且操作系统使用Windows-1252,则需要将其转换为该编码。一种方法是使用iconv库。