C++20 UTF-8 八进制序列字符串文字

问题描述 投票:0回答:1

将遗留代码移植到 C++20 时,我将字符串文字(带有预期的 UTF-8 编码文本)替换为 UTF-8 字符串文字(前缀为

u8
的字符串)。

因此,我遇到了八进制序列的问题,我过去用它来逐字节编码 UTF-8 序列:

同时

"\303\274"
ü
,
的正确编码
u8"\303\274"
最终成为
ü

我进一步调查了这一点,并在 cppreference.com:

上找到了
  1. 对于每个数字转义序列,将

    v
    指定为由转义序列中的数字序列组成的八进制或十六进制数字表示的整数值,并将 T 作为字符串文字的数组元素类型(请参阅上面的表):

    • 如果
      v
      不超过T的可表示值的范围,则转义序列贡献具有值
      v
      的单个代码单元。

(强调我的)

用我自己的话说:在 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 位数字,没有不相关的字符可能会无意中扩展它们 - 与十六进制序列相反。

c++ utf-8 c++20
1个回答
0
投票

用我自己的话说:在 UTF-8 字符串文字中,八进制 (

\ooo
) 和十六进制 (
\xXX
) 转义序列被解释为 Unicode 代码点,类似于 Unicode 序列(
\uXXXX
\UXXXXXXXX
)。

不,这是不正确的。在UTF-8中,一个代码单元表示8位单元,而一个Unicode代码点可能由多个代码单元组成。每个八进制转义序列对应于一个代码unit,这与 Unicode 转义序列不同,Unicode 转义序列对应于代码points

所以 GCC 和 Clang 是正确的,而 MSVC 有 bug。

最新问题
© www.soinside.com 2019 - 2024. All rights reserved.