在C ++中创建字符串文字时,我想知道字符串是如何编码的 - 我可以指定编码格式(UTF-8,16或32),但我想知道编译器如何确定未指定的部分编码。
对于UTF-8,字节排序不相关,我假设UTF-16和UTF-32的字节顺序默认为系统字节排序。这使得标准化。举个例子:
std::string u8foo = u8"Föo";
std::u16string u16foo = u"Föo";
std::u32string u32foo = U"Föo";
在所有三种情况下,至少有两种可能的编码 - 分解或组合。对于更复杂的字符,可能存在多种可能的编码,但我认为编译器会生成一种规范化的形式。
这是一个安全的假设吗?我是否可以事先知道u8foo
和u16foo
中的文字存在哪种规范化?我可以用某种方式指定吗?
我的印象是标准没有定义,并且它是特定于实现的。 GCC如何处理它?其他编译器?
答案 0 :(得分:2)
基本源字符集之外的字符串的解释是依赖于实现的。 (下面的标准报价。)所以没有明确的答案;一个实现甚至不必接受基本集之外的源字符。
规范化涉及将可能多个源代码点映射到可能的多个内部代码点,包括重新排序源字符序列的可能性(例如,如果变音符号不是规范顺序)。这种转换比标准所预期的源→内部转换更复杂,我怀疑尝试它们的编译器不会完全符合。无论如何,我知道没有编译器这样做。
因此,一般来说,您应确保根据所需的规范化表格对您提供给编译器的源代码进行规范化,如果这对您很重要。
在GCC的特定情况下,编译器根据默认语言环境的编码解释源,除非另有说明(使用-finput-charset
命令行选项)。如有必要,它将重新编码为Unicode代码点。但它并没有改变代码点的顺序。因此,如果你给它一个标准化的UTF-8字符串,那就是你得到的。如果你给它一个非标准化的字符串,那也是你得到的。
在这个example on coliru中,第一个字符串被组成,第二个字符串被分解(尽管它们都是一些规范化形式)。 (coliru中第二个示例字符串的渲染似乎与浏览器有关。在我的机器上,chrome正确渲染它们,而firefox将变音符号向左移动一个位置.YMMV。)
C ++标准将基本源字符集(在§2.3/ 1中)定义为字母,数字,五个空格字符(空格,换行符,制表符,垂直制表符和换页符)和符号:
_ { } [ ] # ( ) < > % : ; . ? * + - / ^ & | ~ ! = , \ " ’
它为编译器提供了很多关于它如何解释输入的自由度,以及它如何处理基本源字符集之外的字符。 §2.2第1段(来自C ++ 14草案n4527):
物理源文件字符以实现定义的方式映射到基本源字符集(如果需要,引入行尾指示符的换行符)。接受的物理源文件字符集是实现定义的。不在基本源字符集(2.3)中的任何源文件字符将替换为指定该字符的通用字符名称。 (实现可以使用任何内部编码,只要源文件中遇到的实际扩展字符,以及源文件中表示为通用字符名称的相同扩展字符(例如,使用\ uXXXX表示法),处理等效,除非在原始字符串文字中还原此替换。)
从C ++标准的角度来看,值得补充的是,变音符号是字符。因此,组合的ñ(\ u00d1)是一个字符,分解的ñ(\ u006e \ u0303)是两个字符,无论它看起来如何。
从标准中仔细阅读上述段落表明,虽然编译器可能能够拒绝包含基本源字符集之外的字符的输入,但不允许标准化或不严格为1-1的其他转换。
答案 1 :(得分:0)
Microsoft Visual C ++将保持源文件中使用的规范化。
执行此跨平台时遇到的主要问题是确保编译器使用正确的编码。以下是MSVC如何处理它:
编译器必须使用正确的编码读取源文件。
MSVC没有选择在命令行上指定编码,但依赖于BOM来检测编码,因此它可以读取以下编码:
"\xef\xbb\xbf"
开头(UTF-8&#34; BOM&#34;)在将可执行文件作为字节字符串写入之前,您的unicode字符串将使用某种编码进行编码。
宽文字(L"..."
)始终写为UTF-16。
MSVC 2010您可以使用#pragma execution_character_set("utf-8")
将char
个字符串编码为UTF-8。默认情况下,它们在您的本地代码页中进行编码。 2012年MSVC显然缺少这种实用主义,但它已回到2013年的MSVC。
#pragma execution_character_set("utf-8")
const char a[] = "ŦεŞŧ";
支持Unicode文字(u"..."
和朋友)was only just now introduced with MSVC 2015。