将遗留代码移植到 C++20 时,我将字符串文字(带有预期的 UTF-8 编码文本)替换为 UTF-8 字符串文字(前缀为
u8
的字符串)。
因此,我遇到了八进制序列的问题,我过去用它来逐字节编码 UTF-8 序列:
同时
"\303\274"
是 ü
,u8"\303\274"
最终成为 ü
。
我进一步调查了这一点,并在 cppreference.com:
上找到了(强调我的)
用我自己的话说:在 UTF-8 字符串文字中,八进制 (
\ooo
) 和十六进制 (\xXX
) 转义序列被解释为 Unicode 代码点,类似于 Unicode 序列(\uXXXX
和 \UXXXXXXXX
)。
因此,这对我来说似乎是合理的:对于 UTF-8 字符串文字,Unicode 转义序列应该优于按字节的八进制序列(我过去使用过)。
出于好奇(并且为了演示的目的),我对 coliru 做了一个小测试,并惊讶地发现使用
g++ -std=c++20
,八进制序列仍然被解释为单个字节。考虑到上述内容,我得出结论:
MSVC 似乎是正确的,而 g++ 是错误的。
我制作了一个 MCVE,并在本地 Visual Studio 2019 中运行:
#include <iostream>
#include <string_view>
void dump(std::string_view text)
{
const char digits[] = "0123456789abcdef";
for (unsigned char c : text) {
std::cout << ' '
<< digits[c >> 4]
<< digits[c & 0xf];
}
}
#define DEBUG(...) std::cout << #__VA_ARGS__ << ";\n"; __VA_ARGS__
int main()
{
DEBUG(const char* const text = "\344\270\255");
DEBUG(dump(text));
std::cout << '\n';
DEBUG(const char8_t* const u8text = u8"\344\270\255");
DEBUG(dump((const char*)u8text));
std::cout << '\n';
DEBUG(const char8_t* const u8textU = u8"\u4e2d");
DEBUG(dump((const char*)u8textU));
std::cout << '\n';
}
MSVC的输出:
const char* const text = "\344\270\255";
dump(text);
e4 b8 ad
const char8_t* const u8text = u8"\344\270\255";
dump((const char*)u8text);
c3 a4 c2 b8 c2 ad
const char8_t* const u8textU = u8"\u4e2d";
dump((const char*)u8textU);
e4 b8 ad
(请注意,第 1st 和第 3rd 文字的转储是相同的,而第二个通过将每个八进制序列解释为 Unicode 代码点而产生 UTF-8 序列。)
相同的代码在编译器资源管理器中运行,使用 g++ (13.2):
进行编译const char* const text = "\344\270\255";
dump(text);
e4 b8 ad
const char8_t* const u8text = u8"\344\270\255";
dump((const char*)u8text);
e4 b8 ad
const char8_t* const u8textU = u8"\u4e2d";
dump((const char*)u8textU);
e4 b8 ad
相同的代码在编译器资源管理器中运行,使用 clang (17.0.1):
进行编译const char* const text = "\344\270\255";
dump(text);
e4 b8 ad
const char8_t* const u8text = u8"\344\270\255";
dump((const char*)u8text);
e4 b8 ad
const char8_t* const u8textU = u8"\u4e2d";
dump((const char*)u8textU);
e4 b8 ad
我的结论是否正确:MSVC 根据 C++ 标准正确执行,而不是 g++ 和 clang?
我之前通过网络搜索发现:
使用十六进制转义序列而不是八进制序列不会改变任何内容:编译器资源管理器上的演示。
我更喜欢某种不寻常的八进制序列,因为它们仅限于 3 位数字,没有不相关的字符可能会无意中扩展它们 - 与十六进制序列相反。