如何将字符(char)或字符串(char*std::stringstd::wstring等)与相应的文字进行比较,以确保在不同的运行时环境中可以安全地进行本地化和更改字符编码?

让我们以下面的最小示例作为开始。

using namespace std;
// Get runtime locale and apply it to i/o streams
locale loc( "" );
cout.imbue( loc );
cin.imbue( loc );

// Ask question and compare answer
char c = '\0';
do {
    cout << "Important question [y/n]" << endl;
    cin >> c;
} while( c != 'n' && c != 'y' );

if( c == 'n' ) {
  // execute 'no'-branch
} else {
  // execute 'yes'-branch
}


(我知道该示例可以通过多种方式进行改进。在读取下一个字符之前,应清除输入流,依此类推。但这不是重点。)

我的问题是,将来自环境的字符c与硬编码的文字'n'进行比较,尽管变量char的类型名称不同,但实际上我们不比较字符(或字素),而是比较单个字符字节按位级别。

在编译期间,文字'n'被转换为执行字符集。如果编译器在Linux下为gcc,则默认为UTF-8。但这不能保证,因为该标准仅要求包含某些字符的代码集。因此,实际上每个编译器都可以自由选择合适的字符集。但是无论如何,让我们暂时假设编译器将'n'转换为'\x6e'

但是,运行时环境可以使用完全不同的编码。假设环境使用UTF-16。如果用户键入“ n”,则输入流将填充两个字节序列"\x00\x6e"。因此,cin >> c读取第一个字节'\x00'并将其与'\x6e'进行比较。显然,这不是预期的。

而且,如果我们想将字符串拆分为令牌,情况会变得更糟。它有几个功能(C的strtokboost::tokenize),但是基本上它们都可以完成strtok的工作。它们采用一个输入字符串和一个字符串,这些字符串将用作定界符。但是同样,这些功能不适用于字符而是适用于字节。

让我们举一个简单的例子

strtok( "alice, bob; charlie", ",;" );


有意将第一个字符串拆分为“,”或“;”。此外,让我们假设幸运的是,两个字符串都由相同的字符编码UTF-16编码。尽管两个字符串使用相同的编码,但结果是完全丢失,因为",;"是四个字节的序列"\x00\x2c\x00\x3b",而第一个字符串是40字节的序列,而第二个字节是'\x00'。由于strtok(以及boost::tokenize等)对字节有效,因此结果是虚假的。

我知道也有std::wstring,由于C ++ 11还有std::u16stringstd::u32string,但它们并不是真正的解决方法。 (我不想详细说明它们,因为问题已经足够长了。)

当然,有诸如IBM的ICU之类的第三方库或诸如Qt之类的完整框架可以避免所有这些问题,但是所有这些库都通过定义自己的字符串类来解决该问题。

一方面,这些库大多彼此不兼容,或者如果要合并这些库,则必须进行大量类型转换和字符串复制。另一方面,我通常只编写小的命令行实用程序,并且我不想创建对像Qt这样的非常大的库的依赖,而只是为了完成类似于该问题的第一个示例的任务。

我不敢相信,对于像将字符与'y''n'进行比较这样的琐碎问题,没有“标准”解决方案仅使用C ++标准库。回到我最初的问题:

如何将字符(char)或字符串(char*std::stringstd::wstring等)与相应的文字进行比较,以确保在不同的运行时环境中可以安全地进行本地化和更改字符编码尽可能少依赖其他库?

最佳答案

您在文本模式下打开一个窄字符流,读取一些字符,并将它们与文字进行比较。而已。它按定义工作。根据实现的定义,比较等于'n'的字符是'n'

是什么能保证您的实现理解的'n'字符是ASCII n或EBCDIC n或其他?没有。执行字符集与环境使用的字符集之间的映射是实现定义的。映射可能取决于语言环境,因此您有机会通过设置适当的语言环境在多个映射之间进行选择。您需要查阅实现文档,或者只是盲目地相信映射(下半部分)是由ASCII给出的。幸运的是,理智的实现提供了JustWorks™的映射,而理智的实现无法长期生存。

关于使用UTF-16字符串的示例,除非实现承诺其窄字符集将1:1映射到UTF-16或其一部分(在某些语言环境中),否则请使用wchar_t和适当的语言环境,或(因为C + +11)char16_t和u16文字。这基本上就是他们的目的。

关于c++ - 以本地化安全的方式将字符串/字符与C++中的字符串/字 rune 字进行比较,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/32433452/

10-09 17:29
查看更多