gpt4 book ai didi

c++ - C++ 支持的字符

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:18:12 27 4
gpt4 key购买 nike

用外文写字的时候好像有问题(法文...)

例如,如果我要求输入 std::string 或 char[] 像这样:

std::string s;
std::cin>>s; //if we input the string "café"
std::cout<<s<<std::endl; //outputs "café"

一切安好。

虽然如果字符串是硬编码的
std::string s="café";
std::cout<<s<<std::endl; //outputs "cafÚ"

到底是怎么回事? C++ 支持哪些字符以及如何使其正常工作?是否与我的操作系统(Windows 10)有关?我的 IDE (VS 15)?还是用 C++?

最佳答案

简而言之,如果要在 Windows 10(实际上是任何版本的 Windows)上向/从控制台传递/接收 unicode 文本,则需要使用宽字符串、IE、std::wstring。 Windows 本身不支持 UTF-8 编码。这是一个基本的操作系统限制。

控制台和文件系统访问等基于的整个 Win32 API 仅适用于 UTF-16 编码下的 unicode 字符,并且 Visual Studio 中提供的 C/C++ 运行时不提供任何类型的翻译层来制作这个API UTF-8 兼容。这并不意味着您不能在内部使用 UTF-8 编码,只是意味着当您使用 Win32 API 或使用它的 C/C++ 运行时功能时,您需要在 UTF-8 和 UTF 之间进行转换-16 编码。这很糟糕,但这正是我们现在所处的位置。

有些人可能会指导您使用一系列技巧,使控制台能够使用 UTF-8。不要走这条路,你会遇到很多问题。 Unicode 控制台访问仅正确支持宽字符串。

编辑 : 因为UTF-8/UTF-16 字符串转换很重要,而且C++ 中也没有提供太多帮助,这里是我之前准备的一些转换函数:

///////////////////////////////////////////////////////////////////////////////////////////////////
std::wstring UTF8ToUTF16(const std::string& stringUTF8)
{
// Convert the encoding of the supplied string
std::wstring stringUTF16;
size_t sourceStringPos = 0;
size_t sourceStringSize = stringUTF8.size();
stringUTF16.reserve(sourceStringSize);
while (sourceStringPos < sourceStringSize)
{
// Determine the number of code units required for the next character
static const unsigned int codeUnitCountLookup[] = { 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 4 };
unsigned int codeUnitCount = codeUnitCountLookup[(unsigned char)stringUTF8[sourceStringPos] >> 4];

// Ensure that the requested number of code units are left in the source string
if ((sourceStringPos + codeUnitCount) > sourceStringSize)
{
break;
}

// Convert the encoding of this character
switch (codeUnitCount)
{
case 1:
{
stringUTF16.push_back((wchar_t)stringUTF8[sourceStringPos]);
break;
}
case 2:
{
unsigned int unicodeCodePoint = (((unsigned int)stringUTF8[sourceStringPos] & 0x1F) << 6) |
((unsigned int)stringUTF8[sourceStringPos + 1] & 0x3F);
stringUTF16.push_back((wchar_t)unicodeCodePoint);
break;
}
case 3:
{
unsigned int unicodeCodePoint = (((unsigned int)stringUTF8[sourceStringPos] & 0x0F) << 12) |
(((unsigned int)stringUTF8[sourceStringPos + 1] & 0x3F) << 6) |
((unsigned int)stringUTF8[sourceStringPos + 2] & 0x3F);
stringUTF16.push_back((wchar_t)unicodeCodePoint);
break;
}
case 4:
{
unsigned int unicodeCodePoint = (((unsigned int)stringUTF8[sourceStringPos] & 0x07) << 18) |
(((unsigned int)stringUTF8[sourceStringPos + 1] & 0x3F) << 12) |
(((unsigned int)stringUTF8[sourceStringPos + 2] & 0x3F) << 6) |
((unsigned int)stringUTF8[sourceStringPos + 3] & 0x3F);
wchar_t convertedCodeUnit1 = 0xD800 | (((unicodeCodePoint - 0x10000) >> 10) & 0x03FF);
wchar_t convertedCodeUnit2 = 0xDC00 | ((unicodeCodePoint - 0x10000) & 0x03FF);
stringUTF16.push_back(convertedCodeUnit1);
stringUTF16.push_back(convertedCodeUnit2);
break;
}
}

// Advance past the converted code units
sourceStringPos += codeUnitCount;
}

// Return the converted string to the caller
return stringUTF16;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
std::string UTF16ToUTF8(const std::wstring& stringUTF16)
{
// Convert the encoding of the supplied string
std::string stringUTF8;
size_t sourceStringPos = 0;
size_t sourceStringSize = stringUTF16.size();
stringUTF8.reserve(sourceStringSize * 2);
while (sourceStringPos < sourceStringSize)
{
// Check if a surrogate pair is used for this character
bool usesSurrogatePair = (((unsigned int)stringUTF16[sourceStringPos] & 0xF800) == 0xD800);

// Ensure that the requested number of code units are left in the source string
if (usesSurrogatePair && ((sourceStringPos + 2) > sourceStringSize))
{
break;
}

// Decode the character from UTF-16 encoding
unsigned int unicodeCodePoint;
if (usesSurrogatePair)
{
unicodeCodePoint = 0x10000 + ((((unsigned int)stringUTF16[sourceStringPos] & 0x03FF) << 10) | ((unsigned int)stringUTF16[sourceStringPos + 1] & 0x03FF));
}
else
{
unicodeCodePoint = (unsigned int)stringUTF16[sourceStringPos];
}

// Encode the character into UTF-8 encoding
if (unicodeCodePoint <= 0x7F)
{
stringUTF8.push_back((char)unicodeCodePoint);
}
else if (unicodeCodePoint <= 0x07FF)
{
char convertedCodeUnit1 = (char)(0xC0 | (unicodeCodePoint >> 6));
char convertedCodeUnit2 = (char)(0x80 | (unicodeCodePoint & 0x3F));
stringUTF8.push_back(convertedCodeUnit1);
stringUTF8.push_back(convertedCodeUnit2);
}
else if (unicodeCodePoint <= 0xFFFF)
{
char convertedCodeUnit1 = (char)(0xE0 | (unicodeCodePoint >> 12));
char convertedCodeUnit2 = (char)(0x80 | ((unicodeCodePoint >> 6) & 0x3F));
char convertedCodeUnit3 = (char)(0x80 | (unicodeCodePoint & 0x3F));
stringUTF8.push_back(convertedCodeUnit1);
stringUTF8.push_back(convertedCodeUnit2);
stringUTF8.push_back(convertedCodeUnit3);
}
else
{
char convertedCodeUnit1 = (char)(0xF0 | (unicodeCodePoint >> 18));
char convertedCodeUnit2 = (char)(0x80 | ((unicodeCodePoint >> 12) & 0x3F));
char convertedCodeUnit3 = (char)(0x80 | ((unicodeCodePoint >> 6) & 0x3F));
char convertedCodeUnit4 = (char)(0x80 | (unicodeCodePoint & 0x3F));
stringUTF8.push_back(convertedCodeUnit1);
stringUTF8.push_back(convertedCodeUnit2);
stringUTF8.push_back(convertedCodeUnit3);
stringUTF8.push_back(convertedCodeUnit4);
}

// Advance past the converted code units
sourceStringPos += (usesSurrogatePair) ? 2 : 1;
}

// Return the converted string to the caller
return stringUTF8;
}

我负责将一个 600 万行的旧版 Windows 应用程序转换为支持 Unicode 的令人羡慕的任务,当时它只是为了支持 ASCII(实际上它的开发早于 Unicode),我们使用了 std::string 和 char[ ] 在内部存储字符串。由于根本不可能更改所有内部字符串存储缓冲区,因此我们需要在内部采用 UTF-8,并在使用 Win32 API 时在 UTF-8 和 UTF-16 之间进行转换。这些是我们使用的转换函数。

我强烈建议坚持使用新 Windows 开发支持的内容,这意味着宽字符串。也就是说,您没有理由不能将程序的核心基于 UTF-8 字符串,但是在与 Windows 和 C/C++ 运行时的各个方面进行交互时,它会使事情变得更加棘手。

编辑 2:我刚刚重新阅读了最初的问题,我可以看到我没有很好地回答它。让我提供更多信息来专门回答您的问题。

这是怎么回事?在 Windows 上使用 C++ 进行开发时,当您将 std::string 与 std::cin/std::cout 一起使用时,控制台 IO 是使用 MBCS 编码完成的。这是一种不推荐使用的模式,在该模式下,使用当前选择的 code page 对字符进行编码。在机器上。在这些代码页下编码的值不是 unicode,并且无法与选择了不同代码页的其他系统共享,甚至在代码页更改时也无法与同一系统共享。它在您的测试中完美运行,因为您在当前代码页下捕获输入,并将其显示在同一代码页下。如果您 try catch 该输入并将其保存到文件中,检查将显示它不是 unicode。使用在我们的操作系统中选择的不同代码页重新加载它,文本将显示为损坏。如果您知道文本是用什么代码页编码的,则您只能解释文本。由于这些旧代码页是区域性的,并且它们都不能代表所有文本字符,因此实际上不可能在不同的机器和计算机之间普遍共享文本。 MBCS 早于 unicode 的发展,正是因为这些问题才发明了 unicode。 Unicode 基本上是“一个代码页来统治它们”。您可能想知道为什么 UTF-8 不是 Windows 上可选择的“传统”代码页。我们很多人都想知道同样的事情。我只想说,它不是。因此,您不应该依赖 MBCS 编码,因为在使用它时您无法获得 unicode 支持。在 Windows 上支持 unicode 的唯一选择是使用 std::wstring,并调用 UTF-16 Win32 API。

至于关于字符串被硬编码的示例,首先要了解将非 ASCII 文本编码到源文件中会使您进入特定于编译器的行为领域。在 Visual Studio 中,您实际上可以指定源文件的编码(在 File->Advanced Save Options 下)。在您的情况下,文本与您期望的不同,因为它以 UTF-8 编码(很可能),但如前所述,控制台输出是在您当前选择的代码页上使用 MBCS 编码完成的,这不是UTF-8。从历史上看,您会被建议避免源文件中的任何非 ASCII 字符,并使用\x 符号转义任何字符。今天有C++11 string prefixes and suffixes保证各种编码形式。如果您需要此功能,可以尝试使用这些。我没有使用它们的实际经验,所以我不能建议这种方法是否有任何问题。

关于c++ - C++ 支持的字符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41810586/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com