C ++字符串文字仍然令人困惑

c++ string literal still confusing

我一直在阅读一些关于 Unicode 的文章,并意识到我仍然很困惑到底该怎么做。

作为 Windows 平台上的 c++ 程序员,给我的纪律几乎与任何老师都一样:始终使用 Unicode 字符集;将其模板化或尽可能使用 TCHAR;更喜欢 wchar_t, std::wstring 而不是 char, std::string.

#include <tchar.h>
#include <string>
typedef std::basic_string<TCHAR> tstring;
 // ...
static const char* const s_hello = "핼로"; // bad
static const wchar_t* const s_wchar_hello = L"핼로" // better
static LPCTSTR s_tchar_hello = TEXT("핼로") // even better
static const tstring s_tstring_hello( TEXT("핼로") ); // best

不知何故我搞砸了,我让自己相信如果我说"something",那意味着它是ASCII格式,如果我说L"something"它是Unicode。然后我读到:

Type wchar_t is a distinct type whose values can represent distinct codes for all members of the largest extended character set specified among the supported locales (22.3.1). Type wchar_t shall have the same size, signedness, and alignment requirements (3.11) as one of the other integral types, called its underlying type. Types char16_t and char32_t denote distinct types with the same size, signedness, and alignment as uint_least16_t and uint_least32_t, respectively, in , called the underlying types.

那又怎样?如果我的语言环境说从代码页 949 开始,wchar_t 的扩展是从 949 + 2^(sizeof(wchar_t)*8)?它说话的方式听起来像是“我不在乎你的 c++ 实现是否使用 UTF 编码或什么”。

至少,我可以理解一切都取决于应用程序所在的语言环境。因此我测试了:

#define TEST_OSTREAM_PRINT(x) \
std::cout << "----" << std::endl; \
std::cout << "cout : " << x << std::endl; \
std::wcout << "wcout : " << L##x << std::endl;

int main()
{
    std::ostream& os = std::cout;

    std::cout << " * Info : " << std::endl
              << "     sizeof(char) : " << sizeof(char) << std::endl
              << "     sizeof(wchar_t) : " << sizeof(wchar_t) << std::endl
              << "     littel endian? : " << IsLittelEndian() << std::endl;
    std::cout << " - LC_ALL: " << setlocale(LC_ALL, NULL) << std::endl;
    std::cout << " - LC_CTYPE: " << setlocale(LC_CTYPE, NULL) << std::endl;

    TEST_OSTREAM_PRINT("핼로");
    TEST_OSTREAM_PRINT("おはよう。");
    TEST_OSTREAM_PRINT("你好");
    TEST_OSTREAM_PRINT("resume");
    TEST_OSTREAM_PRINT("résumé");

    return 0;
}

然后输出是:

Info
 sizeof(char) = 1
 sizeof(wchar_t) = 2
 LC_ALL = C
 LC_CTYPE = C
----
cout : 핼로
wcout : ----
cout : おはよう。
wcout : ----
cout : ?好
wcout : ----
cout : resume
wcout : resume
----
cout : r?sum?
wcout : r?um

韩语语言环境的另一个输出:

Info
 sizeof(char) = 1
 sizeof(wchar_t) = 2
 LC_ALL = Korean_Korea.949
 LC_CTYPE = Korean_Korea.949
----
cout : 핼로
wcout : 핼로
----
cout : おはよう。
wcout : おはよう。
----
cout : ?好
wcout : ----
cout : resume
wcout : resume
----
cout : r?sum?
wcout : resume

另一个输出:

Info
 sizeof(char) = 1
 sizeof(wchar_t) = 2
 LC_ALL = fr-FR
 LC_CTYPE = fr-FR
----
cout : CU·I
wcout : ----
cout : ªªªIªeª|¡£
wcout : ----
cout : ?u¿
wcout : ----
cout : resume
wcout : resume
----
cout : r?sum?
wcout : resume

事实证明,如果我没有提供正确的语言环境,无论我使用 char 还是 wchar_t,应用程序都无法处理特定范围的字符。这不仅是问题。 Visual studio 发出警告:

warning C4566: character represented by universal-character-name '\u4F60' cannot be represented in the current code page (949)

我不确定这是在描述我得到的输出结果还是其他内容。

问题。什么是最佳实践,为什么?如何使应用程序 platform/implementation/nation 独立?源代码中的字符串文字到底发生了什么?应用程序如何解释字符串值?

C++ 没有正常的 Unicode 支持。如果不使用第 3 方库,您就无法在 C++ 中编写正常的全球化应用程序。阅读 this insightful SO answer. If you really need to write an application which uses Unicode I'd look at ICU 个图书馆。

在 Windows 上,Microsoft guarantees wchar_t 支持 Unicode,因此 L"핼로" 是将 UTF-16 字符串文字生成为 [=12] 的正确方法=].在其他平台上,这不一定成立,如果您需要代码,您应该使用 C++11 Unicode 字符串文字(u8"..."u"..."U"...")便携——例如,使用 u8"핼로" 生成 UTF-8 编码的 const char*(截至 Visual Studio 2015)。

您遇到的另一个问题是 Visual Studio 如何解释源文件的编码。例如, 在 EUC-KR(代码页 949)中编码为 0xAA 0xAA,这是代码页 1252 (fr-FR) 中 ªª 的编码——也就是说,如果你在 EUC-KR 中保存了包含 的源文件,但在 fr-FR 语言环境中编译它,你的文字将编码 ªª.

如果您需要在您的源代码中包含非 ASCII 字符,您应该将它们保存在具有显式 BOM 的 UTF(即 UTF-8/16/32)中——如 answer to this question 中所述。 =25=]