IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> MSVC C++ UTF-8编程 -> 正文阅读

[C++知识库]MSVC C++ UTF-8编程

除windows平台外大部分其他平台,编译器默认使用的编码都是UTF-8编码,最新版本的Clang编译器只支持UTF-8编码。如果程序需要在多个平台编译运行,则代码必须使用UTF-8。使用UTF-8可以更容易的在多字节字符串(char, std::string)和宽字符(wchar_t std::wstring)直接转换,更容易避免程序乱码,中文路径错误等问题。

Windows上MSVC默认使用的编码是当前系统设置的编码,中文系统默认是GBK。Windows上可以在【控制面板\时钟和区域\区域\管理】中设置默认编码,如下图:

?中文系统默认设置为“中文(简体, 中文)”,编码为GBK。上图中如果勾选红框中的选项,则默认编码会设为UTF-8。默认编码一般不要修改,如果默认编码设置错了,在非Unicode程序中会出现乱码。

C++代码字符编码

MSVC中C++代码字符编码默认与系统一致,中文系统默认是GBK。有两种方法修改为UTF-8编码:

  1. 直接使用UTF-8带BOM的文件格式。

  2. 使用UTF-8(不带BOM)格式并使用编译选项 /source-charset:utf-8。cmake设置方法:

# 必须在add_library , add_executable 前设置,否则无效
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/source-charset:utf-8>") 

使用了UTF-8编码的代码,程序字符串变量并不就使用UTF-8编码,默认MSVC在编译时会将UTF-8编码的字符串转换成程序运行时编码,中文系统为GBK,也就是字符串变量存放的是GBK编码的字符串。

C++运行时字符编码

MSVC中C++运行时字符编码默认与系统一致,中文系统默认是GBK。也有两种方法修改为UTF-8:

  1. 使用字符串字面量,在字符串前加上u8,

char* s1 = u8"abc中文"; std::string s2(u8"abc中文");

?????2. 可以通过使用编译选项 /execution-charset:utf-8修改为UTF-8, cmake设置方法:

# 必须在add_library , add_executable 前设置,否则无效
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/execution-charset:utf-8>") 

这样代码在编译时会将字符串转换为UTF-8,运行时字符串变量中的字符则是UTF-8编码。

控制台显示UTF-8编码

windows控制台字符编码使用的是系统默认的编码,可以通过chcp命令查看。如果控制台的编码不是UTF-8(对应编码:65001),显示UTF-8字符串会出现乱码,可通过chcp 65001命令修改控制台的编码,从而正常显示UTF-8字符串。

更好的方法是修改程序的locale变量,这个会修改C/C++的运行环境,代码如下:

td::locale::global(std::locale(".utf8")); 
// 或者 
// std::setlocale(LC_ALL, ".UTF-8");

因为修改locale变量修改的是C/C++的运行环境,并不会修改Windows系统函数的环境,所以如果调用系统函数WriteConsoleOutput函数,还是会出现乱码。 Windows上有些库(例如:spdlog)会调用WriteConsoleOutput函数,设置的locale还是会出现乱码,还需要设置控制台编码,如下:

std::locale::global(std::locale(".utf8")); 
SetConsoleOutputCP(CP_UTF8);

使用/utf-8

使用编译选项/utf-8 可以同时将代码的编码和程序运行时编码都设置为UTF-8。

参考:https://learn.microsoft.com/en-us/cpp/build/reference/utf-8-set-source-and-executable-character-sets-to-utf-8?view=msvc-170

不要混用字符编码与编译选项

  1. 如果使用的是UTF-8编码格式的代码,需要加上/source-charset:utf-8/utf-8编译选项。

  2. 如果不是UTF-8编码格式的代码,一定不要加/source-charset:utf-8/utf-8编译选项。

具体原因:

如果使用的是UTF-8带BOM的格式的代码,MSVC可以自动识别代码为UTF-8格式。但是因为UTF-8 BOM是Windows的标准其他平台并不认,所以一般使用UTF-8(不带BOM)的格式。如果代码不带BOM,不管当前代码是不是UTF-8,MSVC都会将代码当作当前系统默认的字符编码,编译时不会转码。程序运行时字符变量的内容是UTF-8编码,似乎没问题??但如果此时使用字符串字面量,在字符串前加上u8则会出现问题,编译把一个UTF-8的字符串当作GBK,然后转换成UTF-8,此时必然出现乱码。

Visual Studio保存UTF-8文件时会自动增加BOM,如果不想VS自动添加BOM可以使用editorconfig文件配置。在项目根目录下保存一个文件名为: .editorconfig 的文件,文件内容如下:

# editorconfig.org
 
root = true
 
[*]
charset = utf-8
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
 
[*.md]
trim_trailing_whitespace = false

文件路径

C++中文件保存读取以及C++17增加的filesystem中的char/string使用的是系统默认编码,中文系统默认是GBK。可以通过std::locale::global(std::locale(".utf8"))修改为UTF-8。设置后传入std::fstream和filesystem的字符串参数必须是UTF-8编码,filesystem返回的字符编码也是了UTF-8。

Windows系统函数

通过设置std::locale::global(std::locale(".utf8"))并不会改变Windows系统函数多字节版本的字符编码,Windows系统函数多字节版本依然是根据系统设置的默认编码决定。

如果使用UTF-8编码则涉及字符串的Windows系统函数全部使用宽字符(UTF-16)版本,例如创建文件时使用CreateFileW,而不是 CreateFileA。毕竟UTF-8编码和宽字节字符都是Unicode字符,转换非常方便,也很容易实现。

命令行参数

对于int main(int argc, char* args[]) 接受的的参数args,其编码始终是系统默认编码(中文为GBK),即使通过chcp修改控制台的默认编码其接受到的产生依然是系统默认编码。

除了main函数外,还有另外一个函数 wmain,int wmain(int argc, wchar_t* args[])这个函数args 是宽字符(UTF-16),可以很容易的转换为UTF-8。

对于窗口程序的主函数winMain,也有对应的wWinMain

参考:https://stackoverflow.com/questions/13871617/winmain-and-main-in-c-extended

Windows系统函数使用UTF-8字符串

前面说了程序中Windows系统函数字符串编码默认使用的是根据系统设定的,但实际上是可以通过使用manifest文件修改的。修改后不光系统函数使用UTF-8编码,主函数的命令行参数也是UTF-8编码。

创建一个文件名为:manifest.manifest 的文件,写入以下内容:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <assemblyIdentity type="win32" name="..." version="6.0.0.0"/>
  <application>
    <windowsSettings>
      <activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
    </windowsSettings>
  </application>
</assembly>

将文件 manifest.manifest 加入到编译中,VS设置如下:

Windows上CMake可以识别 *.manifest 文件,只需把manifest文件和源码一样加入到程序中就行,如下:

add_executable(CppTest main.cpp manifest.manifest)

注意这种方法只对Win 10 1903(2019年5月发布)后的版本才有效。

参考:https://learn.microsoft.com/en-us/windows/apps/design/globalizing/use-utf8-code-page

获取系统编码

通过函数GetACP()(ACP->Active Code Page)可以在程序中获取当前系统的编码,其返回值是一个整数。整数对应的编码可以在这里查到https://learn.microsoft.com/en-us/windows/win32/intl/code-page-identifiers。

获取C++ locale 的编码,可以先调用setlocale(LC_ALL, ""),后面调用 setlocale(LC_ALL, NULL),就会返回当前系统的locale字符串。

setlocale(LC_ALL, "");
std::cout << "LC_ALL: " << setlocale(LC_ALL, NULL) << std::endl; 
std::cout << "LC_CTYPE: " << setlocale(LC_CTYPE, NULL) << std::endl;

输出:

参考:https://stackoverflow.com/questions/12170488/how-to-get-current-locale-of-my-environment

字符转换

MultiByteToWideCharWideCharToMultiByte可以在Unicode和非Unicode直接转换。这两个函数中codepag参数可以使用CP_ACP(0),代表当前系统的编码。

总结

Window上MVC使用UTF-8编码:

  1. 代码的格式使用UTF-8(无BOM) 格式

  2. 编译器中设置编译选项

    add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>")
     add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
  3. 程序运行时设置local变量和控制台编码

    std::locale::global(std::locale(".utf8")); SetConsoleOutputCP(CP_UTF8);
  4. Windows系统函数的处理有两种方法

    1. Windows系统函数使用宽字符版本,将UTF-8转化为UTF-16,或者UTF-16转为UTF-8。

    2. 使用manifest文件将系统函数修改为使用UTF-8编码。

  5. 使用wmain/wWinMain获取命令行参数,或使用main/WinMain并用 manifest文件修改为UTF-8编码。使用manifest文件时需要注意有些系统不支持,此时可以通过函数GetACP查询当前编码,如果不是的情况可以使用MultiByteToWideChar转换为Unicode编码。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-12-25 10:47:31  更:2022-12-25 10:52:18 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/22 19:14:57-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码