C++20形式的utf-8字符串转宽字符串,不依赖编译器编码形式

默认的char[]编码都是要看编译器编译选项的,你选了ANSI那它就是ANSI,你选了UTF8那它就是UTF8.
compiler-encoding-option
【注意:经典DevC++只支持ANSI编码(痛苦);上图是小熊猫DevC++,则有这个选项】

这一点对我的代码造成了麻烦。我就是要用utf8字符串,无视编译器编码选项,并输出,怎么搞?

先看什么是麻烦的代码:

#include <windows.h>
#include <stdio.h>// 将UTF-8字符串转换为宽字符串(不一定是UTF-16)
wchar_t* utf8_to_wstr(const char* utf8_string) 
{// 获取UTF-8字符串的长度int len = strlen(utf8_string);// 计算所需缓冲区大小int w_size = MultiByteToWideChar(CP_UTF8, 0, utf8_string, len, NULL, 0);// 分配宽字符串缓冲区wchar_t* w_string = (wchar_t*)malloc((w_size + 1) * sizeof(wchar_t));// 将UTF-8多字节转换为宽字符串MultiByteToWideChar(CP_UTF8, 0, utf8_string, len, w_string, w_size);w_string[w_size] = L'\0';  // 添加NULL终止字符return w_string;
}int main() {const char* utf8_string = "Wormwaker创作";// 转换为wchar_t*wchar_t* w_string = utf8_to_wstr(utf8_string);// 使用MessageBoxW显示UTF-16字符串MessageBoxW(NULL, w_string, L"MessageBoxW", MB_OK);// 释放内存free(w_string);return 0;
}

上述代码字符以char类型存储,编码依赖编译器选项。如果为ANSI,则结果为:
failure
如果为UTF-8,才是正确的结果:
success
· 试想,把含类似于这样一段代码的项目(例如一个软件或是一个游戏)代码发给你一个朋友,他一看运行出来是乱码,他第一反应就是你写的有问题,是你的问题。他基本不会考虑自己的编码选项有问题。你可能还要教他怎么调,这将消耗你宝贵的时间。于是,这段代码可能需要变得兼容一点。


随着时代的进步,C++针对utf编码的字符出现了更新:

C++11

1.添加新字符类型 char16_tchar32_t,分别对应utf-16和utf-32编码。同时也添加了相应的std::basic_string,也就是 std::u16stringstd::u32string.
2.添加三种字符串字面量前缀:u, U, 以及 u8,分别对应utf-16, utf-32, utf-8编码。

注意:此时还没有 char8_t !

这时候就可以写这样的代码了:

char16_t utf16c = u'好';
char32_t utf32c = U'好';
char utf8[] = u8"你好世界";
char16_t utf16[] = u"你好世界";
char32_t utf32[] = U"你好世界";

注意!因为没有 char8_t[],所以u8字符串被存在了char[]里。
而且:

C++ 17

到了C++17才添加了对u8前缀的utf-8字符的支持!也就是说,下面这么写必须 是C++17标准:

char utf8c = u8'a'; // C++17标准
//char utf8c = u8'好';

到这里已经可以实现我们想要的兼容性了,不过到最后再一起说

C++ 20

C++20终于把 char8_t 加入到了基本类型中。现如今所有u8的字符和字符串都必须用char8_t系列存储了,不允许使用char了。 也就是说,应该改成这样:

char8_t uft8c = u8'a';  //C++20
const char8_t* pstrUtf8 = u8"Hello World";
std::u8string sutf8 {u8"Hello Universe"};

当然有char8_t那就肯定也一起出了std::u8string.
basic_string

std::basic_string变化详情 - 跳转链接→


最后就是兼容可靠的代码的书写了:
针对C++17标准:

#include <windows.h>
#include <stdio.h>// 将UTF-8字符串转换为宽字符串(不一定是UTF-16)
wchar_t* utf8_to_wstr(const char* utf8_string) 
{// 获取UTF-8字符串的长度int len = strlen(utf8_string);// 计算所需缓冲区大小int w_size = MultiByteToWideChar(CP_UTF8, 0, utf8_string, len, NULL, 0);// 分配宽字符串缓冲区wchar_t* w_string = (wchar_t*)malloc((w_size + 1) * sizeof(wchar_t));// 将UTF-8多字节转换为宽字符串MultiByteToWideChar(CP_UTF8, 0, utf8_string, len, w_string, w_size);w_string[w_size] = L'\0';  // 添加NULL终止字符return w_string;
}int main() {const char* utf8_string = u8"Wormwaker创作";// 转换为wchar_t*wchar_t* w_string = utf8_to_wstr(utf8_string);// 使用MessageBoxW显示UTF-16字符串MessageBoxW(NULL, w_string, L"MessageBoxW", MB_OK);// 释放内存free(w_string);return 0;
}

就看这么一句就行了:

const char* utf8_string = u8"Wormwaker创作";

这样即使编译器默认以ANSI编码EXE,也会单独把这个字符串以UTF-8编码的,达到了想要的效果。

针对≥C++20标准:

#include <windows.h>
#include <stdio.h>// 将UTF-8字符串转换为宽字符串(不一定是UTF-16)
wchar_t* utf8_to_wstr(const char8_t* utf8_string) 
{// 获取UTF-8字符串的长度int len = strlen((const char*)utf8_string);// 计算所需缓冲区大小int w_size = MultiByteToWideChar(CP_UTF8, 0, (const char*)utf8_string, len, NULL, 0);// 分配宽字符串缓冲区wchar_t* w_string = (wchar_t*)malloc((w_size + 1) * sizeof(wchar_t));// 将UTF-8多字节转换为宽字符串MultiByteToWideChar(CP_UTF8, 0, (const char*)utf8_string, len, w_string, w_size);w_string[w_size] = L'\0';  // 添加NULL终止字符return w_string;
}int main() {const char8_t* utf8_string = u8"Wormwaker创作";// 转换为wchar_t*wchar_t* w_string = utf8_to_wstr(utf8_string);// 使用MessageBoxW显示UTF-16字符串MessageBoxW(NULL, w_string, L"MessageBoxW", MB_OK);// 释放内存free(w_string);return 0;
}

要注意的是
1.

const char8_t* utf8_string = u8"Wormwaker创作";

2.在所有const char* (或LPCSTR)的参数处都要把const char8_t* 强转成const char*.

如果你的编译器支持C++20标准,建议就用这第二种。毕竟在未来的标准下都得这么写。

完美解决!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/294289.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vp与vs联合开发-网口通信(socket)

Socket通信是一种在网络中进行进程间通信的机制。它使用了一种称为套接字&#xff08;Socket&#xff09;的编程接口&#xff0c;通过该接口可以创建、连接、发送和接收数据等操作。 Socket通信中&#xff0c;有两个主要的角色&#xff1a;服务器和客户端。服务器负责监听指定…

十三、W5100S/W5500+RP2040之MicroPython开发<MQTT新版OneNET示例>

文章目录 1. 前言2. 平台操作流程3. WIZnet以太网芯片4. 示例讲解以及使用4.1 程序流程图4.2 测试准备4.3 连接方式4.4 相关代码4.5 烧录验证 5. 注意事项6. 相关链接 1. 前言 在这个智能硬件和物联网时代&#xff0c;MicroPython和树莓派PICO正以其独特的优势引领着嵌入式开发…

【基于激光雷达的路沿检测用于自动驾驶的真值标注】

文章目录 概要主要贡献内容概述实验小结 概要 论文地址&#xff1a;https://arxiv.org/pdf/2312.00534.pdf 路沿检测在自动驾驶中扮演着重要的角色&#xff0c;因为它能够帮助车辆感知道可行驶区域和不可行驶区域。为了开发和验证自动驾驶功能&#xff0c;标注的数据是必不可…

VideoPoet: Google的一种用于零样本视频生成的大型语言模型

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

DPDK单步跟踪(3)-如何利用visual studio 2019和visual gdb来单步调试dpdk

准备工作 因为时间的关系&#xff0c;我想到哪说到哪&#xff0c;可能没那么高的完成度。 但其实有心的人&#xff0c;看到这个标题&#xff0c;就关了本文自己能做了。 why和how to build debug version DPDK,见前两篇。这里我们准备开始。 首先&#xff0c;你有一台linux机…

二维码智慧门牌管理系统升级:安全与便捷并存

文章目录 前言一、系统升级与用户操作记录二、展望与智能门禁未来三、智能科技为未来铺路 前言 科技与门禁系统演进 随着科技的飞速发展&#xff0c;智能门牌系统成为建筑物不可或缺的一部分。其中&#xff0c;二维码智慧门牌管理系统以其独特优势逐渐受到关注。它不仅提升了出…

华为鸿蒙(HarmonyOS):连接一切,智慧无限

华为鸿蒙是一款全场景、分布式操作系统&#xff0c;旨在构建一个真正统一的硬件生态系统。该操作系统于2019年8月首次发布&#xff0c;并被设计为可以应用于各种设备&#xff0c;包括智能手机、智能手表、智能电视、车载系统等多种智能设备。 推荐一套最新版的鸿蒙4.0开发教程 …

构建创新学习体验:企业培训系统技术深度解析

企业培训系统在现代企业中发挥着越来越重要的作用&#xff0c;它不仅仅是传统培训的延伸&#xff0c;更是技术创新的结晶。本文将深入探讨企业培训系统的关键技术特点&#xff0c;并通过一些简单的代码示例&#xff0c;展示如何在实际项目中应用这些技术。 1. 前端技术&#…

MyBatis见解3

8.MyBatis的关联查询 8.3.一对多查询 需求&#xff1a;查询所有用户信息及用户关联的账户信息。 分析&#xff1a;用户信息和他的账户信息为一对多关系&#xff0c;并且查询过程中如果用户没有账户信息&#xff0c;此时也要将用户信息查询出来&#xff0c;此时左外连接查询比…

Java设计模式-原型模式

目录 一、克隆羊问题 二、传统方式解决 三、基本介绍 四、浅拷贝和深拷贝 &#xff08;一&#xff09;浅拷贝介绍 &#xff08;二&#xff09;深拷贝 五、原型模式深拷贝 &#xff08;一&#xff09;重写clone方法 &#xff08;二&#xff09;对象序列化 六、注意事项…

3D模型人物换装系统(二 优化材质球合批降低DrawCall)

3D模型人物换装系统 介绍原理合批材质对比没有合批材质核心代码完整代码修改总结 介绍 本文使用2018.4.4和2020.3.26进行的测试 本文没有考虑法线贴图合并的问题&#xff0c;因为生成法线贴图有点问题&#xff0c;放在下一篇文章解决在进行优化 如果这里不太明白换装的流程可以…

Ignite数据流处理

数据流处理 #1.概述 Ignite提供了一个数据流API&#xff0c;可用于将大量连续的数据流注入Ignite集群&#xff0c;数据流API支持容错和线性扩展&#xff0c;并为注入Ignite的数据提供了至少一次保证&#xff0c;这意味着每个条目至少会被处理一次。 数据通过与缓存关联的数据…