2014-02-12 104 views
1

考虑下面的代码:的Visual C++:越野车towupper

#include <iostream> 
#include <cwctype> 
#include <clocale> 

int main() 
{ 
    wchar_t c = L'\u00ff'; // ÿ LATIN SMALL LETTER Y WITH DIAERESIS 
       // → 0178 Ÿ latin capital letter y with diaeresis 

    std::cout << std::hex << std::showbase; 
#ifdef WIN32 
    const char * lcc = setlocale(LC_ALL, "English"); 
#else 
    const char * lcc = std::setlocale(LC_ALL, "en_US.cp1252"); 
#endif 
    if(lcc) { 
     std::cout << "set locale: " << lcc << std::endl; 
     std::cout << "towupper(" << (std::wint_t)c << ") = " << towupper(c) << '\n'; 
    } else { 
     std::cout << "failed to set locale" << std::endl; 
    } 
} 

如果我编译并在Visual Studio 2010(也2013)运行它,结果是:

set locale: English_United States.1252 
towupper(0xff) = 0x9f 

在Linux上用gcc :

set locale: en_US.cp1252 
towupper(0xff) = 0x178 

towupper的结果在两个平台上是不同的,linux/gcc给出的答案似乎对我来说是正确的,因为t他0x178(Ÿ)是0xff(ÿ)的正确大写Unicode代码点。

但是,0x9f也是Ÿ的代码点,但在使用的Windows-1252代码页中。因此,看起来好像Visual C的towupper会将输入视为一个窄字符,并根据预设的代码页进行解释。

至于我的理解,宽字符应该总是被解释为Unicode代码点,Windows/VC上的UTF-16和Linux/gcc上的UTF-32。我在这里错了吗,还是在Microsoft实施中真的是一个错误?或者只是在这种情况下规格不够严格,而且都可以被视为正确的结果?

+1

您明确地将您的区域设置为CP1252,它是一个8位编码。如果有的话,我会说异常版本是Linux版本,因为在0xff之后应该没有可用的字符(并且在wstring中每个字符至少会浪费1个字节)。 –

+1

MSVC CRT的记录行为:“towupper的大小写转换是特定于locale的”。对于Linux:“这个函数不适合处理Unicode字符”。 –

+0

@ user846250据我所知,towupper应该将输入视为一个Unicode代码点,因此不会根据任何代码页来解释它。所以语言环境的代码页设置应该是不相关的。有一个toupper变体(没有'w')应该根据代码页来解释输入。 –

回答

1

从这个问题的评论看来,似乎没有“真正的”解决方案; C或C++标准在字符编码方面不够严格,所以我们不能对区域敏感的结果有真正的期望。

对于我在Windows上的具体使用情况,CharUpperW竟然是一个可行的选择,虽然它依赖于平台。