不同的字符集有什么用?
What are the different character sets used for?
C++ 标准提到了多个不同的字符集。特别是,它提到了以下字符集:
- 在 2.2 [lex.phases] 项目符号 1 物理源文件字符 及其到 基本源字符集 的映射是提到。
- 在2.2 [lex.phases] bullet 2中提到执行字符集。
- 在2.3[lex.charset]第3段一个基本执行字符集和一个基本执行宽字符集是提到。
- 同节2.3 [lex.charset] 3也提到了一个执行字符集和一个执行宽字符集 .
- 当读取或写入文件时,这些文件使用一些其他字符集。
这些不同的字符集有什么用,它们之间的转换是如何完成的,哪些值取决于语言环境?特别是,字符串文字是如何表示的?
这里是编译器本身使用的不同字符集的分解(所有对标准的引用实际上都是针对 C++14):
- 物理源文件字符 是 C++ 源中使用的字符。这些现在很可能使用某种 Unicode 编码进行编码,例如 UTF-8 or UTF-16. If you are from a European or an American background you may be using ASCII whose characters are conveniently encoded identically in UTF-8 (every ASCII file is a UTF-8 file but not the other way around). The physical source file characters_ may also be something unusual like EBCDIC.
基本源字符集是编译器使用的,至少在概念上是这样。它是从物理源文件字符生成的,并将它们映射到它们各自的基本字符,或者映射到使用 通用字符名称 表示物理源字符的基本字符序列(参见 2.2 [lex.phases] 第 1 段)。基本源字符集是一组96个字符(2.3 [lex.charset]第1段):
a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 _ { } [ ] # ( ) < > % : ; . ? * + - / ^ & | ~ ! = , \ " ’
and the 5 special characters space (' '), horizontal tab (\t), vertical tab (\v), form feed (\f), and newline (\n)
物理源字符集和基本字符集之间的映射是实现定义的。
基本执行字符集和基本执行宽字符集是能够表示由几个特殊字符扩展的基本源字符集:
alert ('\a'), backspace ('\b'), carriage return ('\r'), and a null character ('[=17=]')
非宽版和宽版的区别在于字符是用char
还是wchar_t
表示。
执行字符集和执行宽字符集是基本字符集的实现定义扩展和基本的宽字符集。在 2.3 [lex.charset] 的第 3 段中指出,执行字符集的附加成员和附加成员的值是特定于语言环境的。不清楚指的是哪个语言环境,但我怀疑是指编译期间使用的语言环境。在任何情况下,执行字符集都是实现定义的(也根据 2.3 [lex.charset] 第 3 段)。
字符和字符串文字最初是使用基本源字符集表示的,某些字符可能使用通用字符名称。所有这些都在编译时转换为执行字符集。根据 2.14.3 [lex.ccon] 字符文字,在执行字符集中可以表示为一个 char
。如果需要多个 char
,则可能有条件地支持字符文字(并且它们的类型为 int
)。对于字符串文字,转换在 2.14.5 [lex.string] 中进行了描述。第 9 段指出 UTF-8 字符串文字(例如 u8"hello"
)会产生与 UTF-8 字符串的代码单元相对应的值序列。否则字符和通用字符名称的翻译与字符文字的翻译相同(特别是,它是实现定义的)尽管导致窄字符串的多字节序列的字符只会导致多个字符(这种情况不是必需的支持对于字符文字)。
至此,只考虑编译结果。任何不属于字符或字符串文字的字符都用于指定代码的作用。有趣的问题是文字发生了什么?文字基本上都被翻译成一个实现定义的表示。这是实现定义的意味着它在某个地方记录了应该发生的事情,但它在不同的实现之间可能有所不同。
这对处理来自某处的字符或字符串有何帮助?那么,读取的任何字符或字符串都将转换为相应的执行字符集。特别是,当读取文件时,所有字符都会转换为这种通用表示形式。当然,要使这种转换起作用,需要根据该文件的编码设置用于读取文件的语言环境。如果未明确提及语言环境,则使用最初由系统确定的全局语言环境。初始全局语言环境可能是根据用户偏好以某种方式设置的,例如,不基于环境变量。如果读取的文件使用与此全局语言环境不同的编码,则需要使用与文件编码匹配的相应不同语言环境。
相应地,当使用执行字符集之一写入字符时,这些字符将根据当前语言环境指定的编码进行转换。同样,如果需要特定编码,可能需要替换区域设置。
所有这些实际上意味着在程序内部,所有字符串和字符处理都是使用实现定义的执行字符集进行的。程序读取的所有字符都需要转换为该字符集,所有写入的字符都作为该执行字符集中的字符开始,需要适当地转换为外部编码。当然,在理想的设置中,执行字符集和外部表示之间的转换是同一性,例如,因为执行字符集使用 UTF-8 而外部表示也使用 UTF-8。相应地执行宽字符集,除了在这种情况下将使用 UTF-16(UTF-16 的两个变体之一可以使用 big endian or little endian 表示)。
C++ 标准提到了多个不同的字符集。特别是,它提到了以下字符集:
- 在 2.2 [lex.phases] 项目符号 1 物理源文件字符 及其到 基本源字符集 的映射是提到。
- 在2.2 [lex.phases] bullet 2中提到执行字符集。
- 在2.3[lex.charset]第3段一个基本执行字符集和一个基本执行宽字符集是提到。
- 同节2.3 [lex.charset] 3也提到了一个执行字符集和一个执行宽字符集 .
- 当读取或写入文件时,这些文件使用一些其他字符集。
这些不同的字符集有什么用,它们之间的转换是如何完成的,哪些值取决于语言环境?特别是,字符串文字是如何表示的?
这里是编译器本身使用的不同字符集的分解(所有对标准的引用实际上都是针对 C++14):
- 物理源文件字符 是 C++ 源中使用的字符。这些现在很可能使用某种 Unicode 编码进行编码,例如 UTF-8 or UTF-16. If you are from a European or an American background you may be using ASCII whose characters are conveniently encoded identically in UTF-8 (every ASCII file is a UTF-8 file but not the other way around). The physical source file characters_ may also be something unusual like EBCDIC.
基本源字符集是编译器使用的,至少在概念上是这样。它是从物理源文件字符生成的,并将它们映射到它们各自的基本字符,或者映射到使用 通用字符名称 表示物理源字符的基本字符序列(参见 2.2 [lex.phases] 第 1 段)。基本源字符集是一组96个字符(2.3 [lex.charset]第1段):
a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 _ { } [ ] # ( ) < > % : ; . ? * + - / ^ & | ~ ! = , \ " ’
and the 5 special characters space (' '), horizontal tab (\t), vertical tab (\v), form feed (\f), and newline (\n)
物理源字符集和基本字符集之间的映射是实现定义的。
基本执行字符集和基本执行宽字符集是能够表示由几个特殊字符扩展的基本源字符集:
alert ('\a'), backspace ('\b'), carriage return ('\r'), and a null character ('[=17=]')
非宽版和宽版的区别在于字符是用
char
还是wchar_t
表示。执行字符集和执行宽字符集是基本字符集的实现定义扩展和基本的宽字符集。在 2.3 [lex.charset] 的第 3 段中指出,执行字符集的附加成员和附加成员的值是特定于语言环境的。不清楚指的是哪个语言环境,但我怀疑是指编译期间使用的语言环境。在任何情况下,执行字符集都是实现定义的(也根据 2.3 [lex.charset] 第 3 段)。
字符和字符串文字最初是使用基本源字符集表示的,某些字符可能使用通用字符名称。所有这些都在编译时转换为执行字符集。根据 2.14.3 [lex.ccon] 字符文字,在执行字符集中可以表示为一个
char
。如果需要多个char
,则可能有条件地支持字符文字(并且它们的类型为int
)。对于字符串文字,转换在 2.14.5 [lex.string] 中进行了描述。第 9 段指出 UTF-8 字符串文字(例如u8"hello"
)会产生与 UTF-8 字符串的代码单元相对应的值序列。否则字符和通用字符名称的翻译与字符文字的翻译相同(特别是,它是实现定义的)尽管导致窄字符串的多字节序列的字符只会导致多个字符(这种情况不是必需的支持对于字符文字)。
至此,只考虑编译结果。任何不属于字符或字符串文字的字符都用于指定代码的作用。有趣的问题是文字发生了什么?文字基本上都被翻译成一个实现定义的表示。这是实现定义的意味着它在某个地方记录了应该发生的事情,但它在不同的实现之间可能有所不同。
这对处理来自某处的字符或字符串有何帮助?那么,读取的任何字符或字符串都将转换为相应的执行字符集。特别是,当读取文件时,所有字符都会转换为这种通用表示形式。当然,要使这种转换起作用,需要根据该文件的编码设置用于读取文件的语言环境。如果未明确提及语言环境,则使用最初由系统确定的全局语言环境。初始全局语言环境可能是根据用户偏好以某种方式设置的,例如,不基于环境变量。如果读取的文件使用与此全局语言环境不同的编码,则需要使用与文件编码匹配的相应不同语言环境。
相应地,当使用执行字符集之一写入字符时,这些字符将根据当前语言环境指定的编码进行转换。同样,如果需要特定编码,可能需要替换区域设置。
所有这些实际上意味着在程序内部,所有字符串和字符处理都是使用实现定义的执行字符集进行的。程序读取的所有字符都需要转换为该字符集,所有写入的字符都作为该执行字符集中的字符开始,需要适当地转换为外部编码。当然,在理想的设置中,执行字符集和外部表示之间的转换是同一性,例如,因为执行字符集使用 UTF-8 而外部表示也使用 UTF-8。相应地执行宽字符集,除了在这种情况下将使用 UTF-16(UTF-16 的两个变体之一可以使用 big endian or little endian 表示)。