const std::wstring 是如何编码的,以及如何更改为 UTF-16



我创建了这个最小工作C++示例片段,以便在定义具有德语非 ASCII 字符的字符串时比较std::stringstd::wstring中的字节(通过十六进制表示)。

#include <iostream>
#include <iomanip>
#include <string>
int main(int, char**) {
std::wstring wstr = L"äöüß";
std::string str = "äöüß";
for ( unsigned char c : str ) {
std::cout << std::setw(2) << std::setfill('0') << std::hex << static_cast<unsigned short>(c) << ' ';
}
std::cout << std::endl;
for ( wchar_t c : wstr ) {
std::cout << std::setw(4) << std::setfill('0') << std::hex << static_cast<unsigned short>(c) << ' ';
}
std::cout << std::endl;
return 0;
}

此代码段的输出为

c3 a4 c3 b6 c3 bc c3 9f 
00c3 00a4 00c3 00b6 00c3 00bc 00c3 0178

我在运行Windows 10 64位Pro的PC上运行了它,在16.8.1版本中使用MSVC 2019社区版进行编译,使用构建系统cmake进行以下CMakeLists.txt

cmake_minimum_required(VERSION 3.0.0)
project(wstring VERSION 0.1.0)
set(CMAKE_CXX_STANDARD 17)
include(CTest)
enable_testing()
add_executable(wstring main.cpp)
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)

我读到,std::strings是基于char单字节的类型。我看到我的代码段的输出表明str(std::string变量)是UTF-8编码的。我读到,Microsoft编译器使用wchar_ts 和 2 字节来组成std::wstrings(而不是 4 字节wchar_ts,例如 GNU gcc),因此期望wstr(std::wstring变量)是(任何类型的)UTF-16编码。但是我不明白为什么"ß"(拉丁语sharp s)被编码为我所期望0x00c301780x00df。请问有人告诉我:

  • 为什么会这样?
  • 我如何最终使用 UTF-16 编码std::wstrings(大端序会很好,我不介意 BOM)?我可能需要以某种方式告诉编译器吗?
  • 这是什么编码?

编辑 1

更改了标题,因为它不适合问题(实际上 UTF-8 和 UTF-16 是不同的编码,所以我自己已经是新的答案......

编辑 2

忘了提:我使用上述编译器的amd64目标

编辑 3

如果按照 dxiv 的评论中指出添加/utf-8标志(请参阅他链接的 SO-Post),我会得到所需的输出

c3 a4 c3 b6 c3 bc c3 9f
00e4 00f6 00fc 00df

对我来说看起来像 UTF-16-BE(无 BOM)。由于我在 cmake 命令的正确顺序方面遇到了问题,这是我当前的CmakeLists.txt文件。将add_compile_options命令放在add_executable命令之前很重要(为了方便起见,我添加了通知)

cmake_minimum_required(VERSION 3.0.0)
project(enctest VERSION 0.1.0)
set(CMAKE_CXX_STANDARD 17)
include(CTest)
enable_testing()
if (MSVC)
message(NOTICE "compiling with MSVC")
add_compile_options(/utf-8)
endif()
add_executable(enctest main.cpp)
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)

我发现if-endif方法比生成器语法更具可读性,但编写add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")也可以。

注意:对于Qt-Projects,.pro文件有一个很好的开关(参见这个Qt-Form帖子)

win32 {
QMAKE_CXXFLAGS += /utf-8
}

我问题的第一部分仍然是开放的:"ß"(拉丁语尖锐s)0x00c30178什么编码?

如注释中所述,源.cpp文件采用 UTF-8 编码。如果没有 BOM 表,也没有显式/source-charset:utf-8开关,Visual C++ 编译器默认假定源文件保存在活动代码页编码中。从设置源字符集文档中:

默认情况下,Visual Studio 会检测字节顺序标记,以确定源文件是否采用编码的 Unicode 格式,例如 UTF-16 或 UTF-8。如果未找到字节顺序标记,则假定源文件是使用当前用户代码页编码的,除非您使用/source-charset 选项指定字符集名称或代码页。

äöüß的 UTF-8 编码是C3 A4 C3 B6 C3 BC C3 9F,因此行:

std::wstring wstr = L"äöüß";

被编译器视为:

std::wstring wstr = L"xC3xA4xC3xB6xC3xBCxC3x9F"`;

假设活动代码页是通常的 Windows-1252,则(扩展)字符映射为:

win-1252    char    unicode
xC3       Ã       U+00C3
xA4       ¤       U+00A4
xB6       ¶       U+00B6
xBC       ¼       U+00BC
x9F       Ÿ       U+0178

因此,L"xC3xA4xC3xB6xC3xBCxC3x9F"被翻译成:

std::wstring wstr = L"u00C3u00A4u00C3u00B6u00C3u00BCu00C3u0178"`;

为了避免这种(错误)转换,需要通过传递显式/source-charset:utf-8(或/utf-8)编译器开关来告知Visual C++源文件被编码为UTF-8。对于基于 CMake 的项目,可以使用add_compile_options完成此操作,如 是否可以强制 CMake/MSVC 在没有 BOM 的情况下对源文件使用 UTF-8 编码?C4819.

因此期望wstr(std::wstring变量)是(任何类型的)UTF-16编码

std::wstring不指定编码。它是"宽字符"序列,用于某种宽字符(已定义实现)。

标准库中定义了转换方面,用于与不同的编码进行转换。

相关内容

  • 没有找到相关文章

最新更新