Linux(和 Unix?)中 C++ 中最常见的字符串编码

Alf*_*ach 7 character-encoding conventions string c++

为了创建一个在 Windows 和 Linux 之间源代码级可移植并能很好地处理国际化的 C++ 程序,恕我直言,需要考虑三种主要编码:

  • C++ 源代码的编码。
  • 外部数据的编码。
  • 字符串和文字的编码。

对于 C++ 源代码,实际上没有任何替代 UTF-8 和 BOM 的方法,至少如果标准输入和宽字符串文字应该在 Windows 平台上工作。没有 BOM 的 UTF-8 导致 Microsoft 的 Visual C++ 编译器为源代码假定 Windows ANSI 编码,这对于通过 UTF-8 输出很好std::cout,在有限的程度上起作用(Windows 控制台窗口在这里有很多错误)。但是,然后通过输入std::cin不起作用。

而对于外部数据UTF-8似乎是事实上的标准。

但是,内部文字和字符串呢?在这里,我的印象是编码为 UTF-8 的窄字符串是 Linux 中的常见约定。但最近有两个不同的人提出了不同的说法,一个声称 Linux 中国际应用程序中内部字符串的通用约定是 UTF-32,另一个只是声称 Unix 和 Linux 在这方面存在一些未指明的差异。

作为一个在业余爱好基础上稍微摆弄一下旨在抽象出 Windows/Linux 在这方面的差异的微型库的人,我……不得不具体问一下

  • 在程序中表示字符串的常见 Linux 约定是什么?

我很确定有一个普遍的约定,这种约定非常普遍,以至于这个问题有一个真正的答案™。

一个示例显示例如如何在 Linux 上传统地反转字符串(直接使用 UTF-8 很复杂,但大概是由 Linux 中的事实上的标准函数完成的?),也很好,即,作为问题,这个 C++ 程序的 Linux 传统版本是什么(给定的代码适用于 Latin-1 作为 C++ 窄文本执行字符集):

#include <iostream>
#include <algorithm>
#include <string>
using namespace std;

#define STATIC_ASSERT( cond )   static_assert( cond, #cond )

int main()
{
    string line;
    if( getline( cin, line ) )
    {
        static char const aSingleChar[] = "æ";
        STATIC_ASSERT( sizeof( aSingleChar ) - 1 == 1 );
        reverse( line.begin(), line.end() );

        cout << line << endl;
    }
}
Run Code Online (Sandbox Code Playgroud)

小智 6

这只是部分答案,因为您的问题相当广泛。

C++ 定义了一个“执行字符集”(实际上是两个,一个窄的和一个宽的)。

当您的源文件包含以下内容时:

char s[] = "Hello";
Run Code Online (Sandbox Code Playgroud)

然后根据执行编码简单地查找字符串文字中字母的数字字节值。(单独的执行编码适用于分配给宽字符常量的数值L'a'。)

所有这些都是在将源代码文件初始读取到编译过程中的过程中发生的。一旦进入,C++ 字符只不过是字节,没有附加语义。(类型名称char一定是 C 派生语言中最严重的用词不当之一!)

有在C ++ 11,部分异常的文字u8""u""U""确定得到的字符串元素的值(即所得到的值是全局明确的和平台无关的),但是这并不影响如何输入源代码被解释.

一个好的编译器应该允许您指定源代码编码,因此即使您在 EBCDIC 机器上的朋友向您发送她的程序文本,这也不是问题。GCC 提供以下选项:

  • -finput-charset: 输入字符集,即源代码文件是如何编码的
  • -fexec-charset: 执行字符集,即如何编码字符串文字
  • -fwide-exec-charset:宽执行字符集,即如何编码宽字符串文字

GCCiconv()用于转换,因此任何支持的编码iconv()都可以用于这些选项。

之前写过关于 C++ 标准提供的一些处理文本编码的不透明工具。


示例:取上面的代码,char s[] = "Hello";. 假设源文件是ASCII(即输入编码是ASCII)。然后编译器读取99,并将其解释为c,依此类推。当涉及到文字时,它会读取72,将其解释为H。现在它将字节值存储在H由执行编码确定的数组中(72如果是 ASCII 或 UTF-8)。写入时\xFF,编译器读取99 120 70 70,将其解码为\xFF,然后写入255数组。


Gil*_*il' 6

对于外部表示,UTF-8 绝对是标准。一些 8 位编码仍然很强大(主要在欧洲),一些 16 位编码仍然很强大(主要在东亚),但它们显然是遗留编码,正在缓慢退出。UTF-8 不仅是 unix 上的标准,也是网络上的标准。

对于内部表示,没有这种压倒性的标准。如果您环顾四周,您会发现一些 UTF-8、一些 UCS-2、一些 UTF-16 和一些 UCS-4。

  • UTF-8 的优势在于它匹配通用表示,并且它是 ASCII 的超集。特别是,它是此处唯一的空字符对应空字节的编码,如果您有 C API(包括 unix 系统调用和标准库函数),这一点很重要。
  • UCS-2 是一个历史遗留问题。它很有吸引力,因为它被认为是一种固定宽度的编码,但它不能代表所有的 Unicode,这是一个障碍。
  • UTF-16 的主要声望是 Java 和 Windows API。如果您为 unix 编程,Unix API(喜欢 UTF-8)比 Windows API 更相关。只有面向与 UTF-16 之类的 API 交互的程序才倾向于使用 UTF-16。
  • UCS-4 很有吸引力,因为它看起来像一种固定宽度的编码。问题是,它不是,真的。因为组合字符,所以没有固定宽度的 Unicode 编码这样的东西。
  • 还有wchar_t。问题是,在某些平台上是 2 个字节,在其他平台上是 4 个字节,并且它所代表的字符集没有具体说明。由于 Unicode 是事实上的标准字符集,较新的应用程序倾向于避开wchar_t.

在 unix 世界中,压倒它们的论点通常是与 unix API 的兼容性,指向 UTF-8。然而,它不是通用的,因此对于您的库是否需要支持其他编码没有是或否的答案。

在这方面,unix 变体之间没有区别。Mac OS X 更喜欢分解的字符,以便具有规范化的表示,因此您可能也想这样做:它会在 OSX 上节省一些工作,而在其他 unice 上则无关紧要。

请注意,UTF-8 中没有 BOM 这样的东西。字节顺序标记仅对超字节大小的编码有意义。UTF-8 编码文件以字符 U+FEFF 开头的要求特定于一些 Microsoft 应用程序。