0

如何比较字符(char)或字符串(char*std::stringstd::wstring,等等),这是在不同的运行环境中可以安全地进行本地化和改变字符编码?比较字符串/字符在本地化与相应的文字这样的字符串/字符用C字面++安全的方式

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

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的strtok,boost::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,等)与相应的文字,使得它是安全的定位和不同的字符编码中不同的运行时间环境对其他库的依赖性很小

+0

“我不能相信,对于比较字符'y'或'n'这样的小问题,没有”标准“的解决方案,只使用C++标准库。” - 不幸的是,情况正是如此。 – dgel

回答

0

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

什么保证'n'字符,您的实施理解是ASCII n或EBCDIC n或什么?没有。执行字符集与环境使用的字符集之间的映射是实现定义的。该映射可能与语言环境有关,因此您可以通过设置适当的语言环境来选择几种映射。你需要查阅你的实现文档,或者只是盲目地相信(下半部分)映射是由ASCII给出的。幸运的是,理智的实现提供了一个映射,JustWorks ™,疯狂的实现不能长存。

关于与一个UTF-16串的例子,除非实现承诺,其字符集映射1:1至UTF-16或其一部分(在一些区域设置),可以使用wchar_t的和适当的区域设置,或(自C++ 11以来)char16_t和u16文字。这基本上是他们的目的。

+0

我是否正确理解您要声明'cin >> c'已经执行从运行时字符集到内部字符集的转换,以便'c =='n''做正确的事情?但是,如果这种隐式转换失败会发生什么问题?例如,因为用户输入了一个不适合单个“char”的字符?在这种情况下,“cin >> c”是否引发异常? – user2690527

+0

没有“内部字符集”和“运行时字符集”,只有一个执行字符集。除非使用自定义的'codecvt'方面,否则不存在字符到字符的转换。微软的实施可能会或可能不会有不同的表现。无论如何,基本执行字符集的成员都不是与语言环境相关的。 –