Solaris上的UCS-4到多字节转换

时间:2012-04-25 16:26:23

标签: c++ unicode solaris multibyte

为什么这段代码:

char a[10]; 
wchar_t w[10] = L"ä"; // German a Umlaut
int e = wcstombs(a, w, 10);

返回e == -1?

我在Solaris 11上使用Oracle Solaris Studio 10.语言环境是Latin-1,其中包含德语元音。我发现的所有文档(对我而言)表明转换应该成功。

如果我这样做:

char a[10] = "ä"; // German a Umlaut
wchar_t w[10];
int e = mbstowcs(w, a, 10);
e = wcstombs(a, w, 10);

没有错误,但结果是错误的。 (上部A的一些变体。)

我也尝试了类似结果的wstostr。

2 个答案:

答案 0 :(得分:3)

1)验证正确的值是否进入wchar_t。生成宽字符串文字的编译器必须将L"ä"从源代码编码转换为宽执行字符集。

2)验证程序的语言环境是否正确。您可以使用printf("%s\n", setlocale(LC_ALL, NULL));

执行此操作

我怀疑问题是1)因为对我来说,即使程序的语言环境设置不正确,我仍然得到预期的输出。为避免源代码编码出现问题,您可以转义非{ascii字符,如L"\x00E4"

 1  #include <iostream>
 2  #include <clocale>
 3
 4  int main () {
 5    std::printf("%s\n", std::setlocale(LC_ALL, NULL));   // prints "C"
 6
 7    char a[10];
 8    wchar_t w[10] = L"\x00E4"; // German a Umlaut
 9    std::printf("0x%04x\n", (unsigned)w[0]);             // prints "0x00e4"
10
11    std::setlocale(LC_ALL, "");
12    printf("%s\n", std::setlocale(LC_ALL, NULL));        // print something that indicates the encoding is ISO 8859-1
13    int e = std::wcstombs(a, w, 10);
14    std::printf("%i 0x%02x\n", e, (unsigned char)a[0]);  // print "1 0xe4"
15  }
16



C和C ++程序中的字符集

在源代码中,您可以使用“源字符集”中的任何字符,它是“基本源字符集”的超集。编译器将字符串和字符文字中的字符从源字符集转换为执行字符集(或宽字符串和字符文字的宽执行字符集)。

问题是源字符集是依赖于实现的。通常,编译器只需知道您对源代码使用的编码,然后它将接受来自该编码的任何字符。 GCC具有用于设置源编码的命令行参数,Visual Studio将假定源位于用户的代码页中,除非它检测到UTF-8或UTF-16的所谓Unicode签名之一,并且Clang当前始终使用UTF- 8。

一旦编译器为您的代码使用正确的源字符集,它将在“执行字符集”中生成字符串和字符文字。执行字符集是基本源字符集的另一个超集,也是依赖于实现的。 GCC使用命令行参数来设置执行字符集,VS使用用户的语言环境,而Clang使用UTF-8。

因为源字符集是依赖于实现的,所以在基本集之外写入字符的可移植方法是使用十六进制编码直接指定要在执行中使用的数值,或者(如果您不使用C89 / 90)使用通用字符名称(UCN),它们被转换为执行字符集(或在宽字符串和字符文字中使用时的宽执行字符集)。 UCN看起来像\ uNNNN或\ UNNNNNNNN,并使用代码点值NNNN或NNNNNNNN指定Unicode字符集中的字符。 (请注意,C99和C ++ 11禁止您使用代理代码点,如果您希望BMP外部的字符只是使用\ U直接写入字符的值。)

源代码和执行字符集在编译时确定,不会根据运行程序的系统的区域设置进行更改。也就是说,程序区域设置使用不一定与执行字符集匹配的其他编码。但是,宽执行字符集应对应于受支持的语言环境使用的宽字符编码。


Solaris Studio的行为

Oracle的Solaris编译器具有非常简单的行为。对于窄字符串和字符文字,没有指定特定的源编码,源代码中的字节只是直接用作执行文字。这实际上意味着执行字符集与源文件的编码相同。对于宽字符文字,使用系统区域设置转换源字节。这意味着您必须使用区域设置编码保存源文件才能获得正确的宽文字。

我怀疑您的源代码是以除了语言环境指定的编码之外的编码保存的,因此您的编译器无法从L"ä"生成正确的宽字符串文字。您的编辑器可能正在使用UTF-8。您可以使用以下程序进行检查。

 1  #include <iostream>
 2  #include <clocale>
 3
 4  int main () {
 5    wchar_t w[10] = L"ä"; // German a Umlaut
 6    std::printf("0x%04x 0x%04x\n", (unsigned)w[0], (unsigned)w[1]);
 7  }
 8

由于wcstombs可以正确地将宽字符0x00E4转换为'ä'的latin-1编码,因此您希望上面显示0x00E4 0x0000。如果源代码编码是UTF-8,那么您应该看到0x00C3 0x00A4

答案 1 :(得分:1)

您可能必须将语言环境设置为了解德语。具体来说,您需要ctype方面。

试试这个:

setlocale( LC_ALL, ".1252" );

或具体地说:

setlocale( LC_CTYPE, ".1252" );

您可能需要搜索比“.1252”更好的代码页。祝你好运。

上面的代码页示例是Windows。在Unixy系统上,为代码页尝试“de_DE”。