首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >const std::wstring是如何编码的,以及如何更改为UTF-16

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

Stack Overflow用户
提问于 2020-11-30 20:25:47
回答 2查看 591关注 0票数 2

我创建了这个最小工作的C++示例片段,以比较std::stringstd::wstring中的字节(通过它们的十六进制表示)来定义带有两种类型的德语非ASCII字符的字符串。

代码语言:javascript
复制
#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;
}

这个片段的输出是

代码语言:javascript
复制
c3 a4 c3 b6 c3 bc c3 9f 
00c3 00a4 00c3 00b6 00c3 00bc 00c3 0178

我在运行自己的Windows 10 64位Pro的PC上运行这个程序,使用MSVC 2019 Community 在16.8.1版本中编译,使用构建系统cmake和下面的CMakeLists.txt

代码语言:javascript
复制
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编码的。我读到,微软编译器使用带有两个字节的wchar_t组成std::wstrings (而不是GNU的4字节wchar_ts ),因此希望wstr ( std::wstring变量)是(任何类型的) UTF-16编码的。但是我不明白为什么“0x00c30178”(拉丁语夏普s)被编码为0x00df,而不是我所期望的0x00df。请有人告诉我:

  • 为什么会发生这种事?
  • 我如何结束UTF-16编码的std::wstrings (大端将很好,我不介意BOM)?我可能需要以某种方式告诉编译器吗?
  • 这是什么样的编码?

编辑1

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

编辑2

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

编辑3

如果像dxiv的注释(请参阅/utf-8 )中指出的那样添加/utf-8标志,我将得到所需的输出

代码语言:javascript
复制
c3 a4 c3 b6 c3 bc c3 9f
00e4 00f6 00fc 00df

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

代码语言:javascript
复制
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-项目.pro文件有一个很好的开关(参见这个Qt格式的帖子)

代码语言:javascript
复制
win32 {
    QMAKE_CXXFLAGS += /utf-8
}

我问题的第一部分仍然是开放的:“0x00c30178”(拉丁夏普s)的编码是什么?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-12-01 16:41:34

正如注释中所阐明的,源文件.cpp文件是UTF-8编码的.如果没有BOM,并且没有显式的/source-charset:utf-8开关,VisualC++编译器默认假设源文件保存在活动代码页编码中。来自设置源字符集文档:

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

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

代码语言:javascript
复制
    std::wstring wstr = L"äöüß";

编译器将其视为:

代码语言:javascript
复制
    std::wstring wstr = L"\xC3\xA4\xC3\xB6\xC3\xBC\xC3\x9F"`;

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

代码语言:javascript
复制
    win-1252    char    unicode

      \xC3       Ã       U+00C3
      \xA4       ¤       U+00A4
      \xB6       ¶       U+00B6
      \xBC       ¼       U+00BC
      \x9F       Ÿ       U+0178

因此,L"\xC3\xA4\xC3\xB6\xC3\xBC\xC3\x9F"被翻译为:

代码语言:javascript
复制
    std::wstring wstr = L"\u00C3\u00A4\u00C3\u00B6\u00C3\u00BC\u00C3\u0178"`;

为了避免这种(Mis)转换,需要通过传递一个显式的C++ (或/utf-8)编译器开关将源文件编码为UTF-8。对于基于CMake的项目,可以使用add_compile_options完成,如有可能迫使CMake/MSVC在没有BOM的情况下对源文件使用UTF-8编码?C4819所示。

票数 5
EN

Stack Overflow用户

发布于 2020-11-30 20:50:09

因此,wstr ( std::wstring变量)将被(任何类型的) UTF-16编码。

std::wstring不指定编码。它是一个“宽字符”序列,用于某些类型的宽字符(这些字符是实现定义的)。

标准库中定义了转换方面,用于转换到/从不同的编码。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65080667

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档