【问题标题】:How does file encoding affect C++11 string literals?文件编码如何影响 C++11 字符串文字?
【发布时间】:2011-07-22 18:40:27
【问题描述】:

您可以在 C++11 中编写 UTF-8/16/32 字符串文字,方法是分别在字符串文字前加上 u8/u/U。编译器必须如何解释在这些新类型的字符串文字中包含非 ASCII 字符的 UTF-8 文件?我知道该标准没有指定文件编码,仅这一事实就会使源代码中的非 ASCII 字符的解释完全未定义的行为,从而使该功能变得不那么有用。

我知道您仍然可以使用 \uNNNN 转义单个 unicode 字符,但是对于通常包含多个 unicode 字符的完整俄语或法语句子来说,这不是很可读。

我从各种来源了解到,u 在当前 Windows 实现上应该等同于 L,在例如,U Linux 实现。因此,考虑到这一点,我还想知道旧字符串文字修饰符所需的行为是什么......

对于代码示例猴子:

string utf8string a = u8"L'hôtel de ville doit être là-bas. Ça c'est un fait!";
string utf16string b = u"L'hôtel de ville doit être là-bas. Ça c'est un fait!";
string utf32string c = U"L'hôtel de ville doit être là-bas. Ça c'est un fait!";

在理想情况下,所有这些字符串都会产生相同的内容(如:转换后的字符),但我使用 C++ 的经验告诉我,这绝对是实现定义的,可能只有第一个会做我想要的.

【问题讨论】:

    标签: c++ encoding c++11 string-literals


    【解决方案1】:

    在 GCC 中,使用 -finput-charset=charset:

    设置输入字符集,用于从输入文件的字符集转换为GCC使用的源字符集。如果 locale 没有指定,或者 GCC 无法从 locale 中获取此信息,则默认为 UTF-8。这可以被语言环境或此命令行选项覆盖。目前,如果存在冲突,命令行选项优先。 charset 可以是系统的“iconv”库例程支持的任何编码。

    还可以查看-fexec-charset-fwide-exec-charset 选项。

    最后,关于字符串字面量:

    char     a[] = "Hello";
    wchar_t  b[] = L"Hello";
    char16_t c[] = u"Hello";
    char32_t d[] = U"Hello";
    

    字符串字面量的大小修饰符(LuU)仅决定字面量的类型

    【讨论】:

    • 你需要在这些文字前面加上const
    • @Nicol 不。即使假设您的意思是要声明的变量,也不。
    • @Nicol:为什么是谁? char x[] = "a"; x[0] = b;
    • 如果我没看错,您的回答似乎与标准相矛盾。特别是,标准会谈例如“使用 UTF-8 编码的给定字符初始化”“单个 c-char 可能以代理对的形式产生多个 char16_t 字符”。这强烈表明大小修饰符不仅确定类型,还确定引号内的编码(在第一种情况下明确说明 UTF-8,在第二种情况下暗示 UTF-16)。
    • @Damon:我不确定。事实上,有一个专门针对 u8 文字的提议,以允许需要 UTF-8 的语法形式。
    【解决方案2】:

    编译器必须如何解释在这些新类型的字符串文字中包含非 ASCII 字符的 UTF-8 文件。我知道该标准没有指定文件编码,仅这一事实就会使源代码中的非 ASCII 字符的解释完全未定义的行为,从而使该功能变得不那么有用。

    来自 n3290,2.2 翻译阶段 [lex.phases]

    物理源文件字符被映射,在一个 实现定义的方式,以基本的源字符集 (为行尾指示符引入换行符)如果 必要的。接受的物理源文件字符集是 实现定义。 [这里有一些关于三元组的内容。] 任何来源 不在基本源字符集(2.3)中的文件字符被替换 通过指定该字符的通用字符名称。 (一个 实现可以使用任何内部编码,只要实际 源文件中遇到的扩展字符,和 扩展字符在源文件中表示为 通用字符名称(即,使用 \uXXXX 表示法),是 同等处理,除非此替换在 原始字符串文字。)

    有很多标准术语用于描述实现如何处理编码。以下是我尝试更简单、逐步地描述所发生的事情:

    物理源文件字符被映射,在一个 实现定义的方式,以基本的源字符集[...]

    文件编码问题是手动解决的;标准只关心基本的源字符集,并为实现留出空间。

    任何来源 不在基本源字符集(2.3)中的文件字符被替换 通过指定该字符的通用字符名称。

    基本源集是一个简单的允许字符列表。 它不是 ASCII(见进一步)。不在此列表中的任何内容都将“转换”(至少在概念上)为 \uXXXX 表单。

    所以无论使用哪种文字或文件编码,源代码在概念上都转化为基本字符集+一堆\uXXXX。我从概念上说是因为实现实际所做的通常更简单,例如因为他们可以直接处理 Unicode。重要的部分是标准所称的扩展字符(即不是来自基本源集)在使用中应该与其等效的\uXXXX 形式没有区别。请注意,C++03 可用于例如EBCDIC 平台,因此您在 ASCII 方面的推理从一开始就有缺陷。

    最后,我描述的过程也发生在(非原始)字符串文字上。这意味着您的代码与您编写的代码相同:

    string utf8string a = u8"L'h\u00F4tel de ville doit \u00EAtre l\u00E0-bas. \u00C7a c'est un fait!";
    string utf16string b = u"L'h\u00F4tel de ville doit \u00EAtre l\u00E0-bas. \u00C7a c'est un fait!";
    string utf32string c = U"L'h\u00F4tel de ville doit \u00EAtre l\u00E0-bas. \u00C7a c'est un fait!";
    

    【讨论】:

    • 这很有趣。 u8 文字中的 \u00F4 是否实际上扩展为两个字节?
    • @Kerrek 我对我的实现进行了测试,"\u8XXXX" 的大小确实可以严格大于 2。我没有为此引用标准,因为我不确定在哪里可以看到“以 u8 开头的字符串文字,例如 u8”asdf”,是一个 UTF-8 字符串文字,并使用给定的字符初始化为以 UTF-8 编码。” (来自 2.14.5 字符串文字 [lex.string],第 7 段)。这很容易成为一个单独的问题。
    • 即使是微弱的 U+F4 在 UTF-8 中也已经是两个字节了——这很酷,我没有意识到在新的 C++ 中实际上有真正的 UTF 支持(除了提供数据类型) .好的!如果您通过\U0010FFFFutf16string 会发生什么?
    • @Kerrek 即使是最弱的u8"\u0001" 也是大小为2,因为空字节;)。对于 u"" 文字,标准明确提到(进一步的一些段落)“单个 c-char 可能以代理对的形式产生多个 char16_t 字符。”,但这已经达到我对 UTF-16 知识的限制.如果您愿意,我正在聊天中进一步讨论。
    【解决方案3】:

    原则上,编码问题仅在您通过使字符串对人类可见来输出字符串时才重要,这不是编程语言如何定义的问题,因为它的定义仅涉及编码计算。因此,当您决定您在编辑器中看到的内容是否与您在输出中看到的相同(任何类型的图像,无论是在屏幕上还是在 pdf 中)时,您应该问自己哪个约定假设您的用户交互库和操作系统的编码方式。 (例如,这种信息for Qt5:对于 Qt5,如果您的 QStrings 的老式字符串文字的内容是在您的源文件中编码为 utf8,除非您在应用程序执行过程中打开另一个设置)。

    作为结论,我认为 Kerrek SB 是对的,而 Damon 是错的:确实,在代码中指定字面量的方法应该指定它的类型,而不是源文件中用于填充其内容的编码,因为字面量的类型与对其进行的计算有关。像u"string" 这样的东西只是一个“unicode codeunits”数组(即char16_t 类型的值),无论操作系统或任何其他服务软件后来对它们做什么,也不管它们的工作是为您或其他用户寻找的.您只是遇到了为自己添加另一个约定的问题,它在计算中的数字的“含义”(即它们呈现 Unicode 的代码)和它们在您在文本编辑器中工作时在屏幕上的表示之间建立了对应关系.作为程序员,您如何以及是否使用该“含义”是另一个问题,您如何强制执行其他对应关系自然是实现定义的,因为它与编码计算无关,仅与工具使用的舒适性有关.

    【讨论】:

      猜你喜欢
      • 2014-12-31
      • 1970-01-01
      • 2016-05-02
      • 2023-03-20
      • 2015-05-08
      • 2016-04-05
      • 2017-04-11
      • 1970-01-01
      • 2019-02-25
      相关资源
      最近更新 更多