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

2023-12-21 09:28

本文主要是介绍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标准,建议就用这第二种。毕竟在未来的标准下都得这么写。

完美解决!

这篇关于C++20形式的utf-8字符串转宽字符串,不依赖编译器编码形式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/519437

相关文章

Windows下C++使用SQLitede的操作过程

《Windows下C++使用SQLitede的操作过程》本文介绍了Windows下C++使用SQLite的安装配置、CppSQLite库封装优势、核心功能(如数据库连接、事务管理)、跨平台支持及性能优... 目录Windows下C++使用SQLite1、安装2、代码示例CppSQLite:C++轻松操作SQ

C++中RAII资源获取即初始化

《C++中RAII资源获取即初始化》RAII通过构造/析构自动管理资源生命周期,确保安全释放,本文就来介绍一下C++中的RAII技术及其应用,具有一定的参考价值,感兴趣的可以了解一下... 目录一、核心原理与机制二、标准库中的RAII实现三、自定义RAII类设计原则四、常见应用场景1. 内存管理2. 文件操

C++中零拷贝的多种实现方式

《C++中零拷贝的多种实现方式》本文主要介绍了C++中零拷贝的实现示例,旨在在减少数据在内存中的不必要复制,从而提高程序性能、降低内存使用并减少CPU消耗,零拷贝技术通过多种方式实现,下面就来了解一下... 目录一、C++中零拷贝技术的核心概念二、std::string_view 简介三、std::stri

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM

Python pip下载包及所有依赖到指定文件夹的步骤说明

《Pythonpip下载包及所有依赖到指定文件夹的步骤说明》为了方便开发和部署,我们常常需要将Python项目所依赖的第三方包导出到本地文件夹中,:本文主要介绍Pythonpip下载包及所有依... 目录步骤说明命令格式示例参数说明离线安装方法注意事项总结要使用pip下载包及其所有依赖到指定文件夹,请按照以

C++ 函数 strftime 和时间格式示例详解

《C++函数strftime和时间格式示例详解》strftime是C/C++标准库中用于格式化日期和时间的函数,定义在ctime头文件中,它将tm结构体中的时间信息转换为指定格式的字符串,是处理... 目录C++ 函数 strftipythonme 详解一、函数原型二、功能描述三、格式字符串说明四、返回值五

Springboot3+将ID转为JSON字符串的详细配置方案

《Springboot3+将ID转为JSON字符串的详细配置方案》:本文主要介绍纯后端实现Long/BigIntegerID转为JSON字符串的详细配置方案,s基于SpringBoot3+和Spr... 目录1. 添加依赖2. 全局 Jackson 配置3. 精准控制(可选)4. OpenAPI (Spri

C++作用域和标识符查找规则详解

《C++作用域和标识符查找规则详解》在C++中,作用域(Scope)和标识符查找(IdentifierLookup)是理解代码行为的重要概念,本文将详细介绍这些规则,并通过实例来说明它们的工作原理,需... 目录作用域标识符查找规则1. 普通查找(Ordinary Lookup)2. 限定查找(Qualif

使用Python实现base64字符串与图片互转的详细步骤

《使用Python实现base64字符串与图片互转的详细步骤》要将一个Base64编码的字符串转换为图片文件并保存下来,可以使用Python的base64模块来实现,这一过程包括解码Base64字符串... 目录1. 图片编码为 Base64 字符串2. Base64 字符串解码为图片文件3. 示例使用注意

Java -jar命令如何运行外部依赖JAR包

《Java-jar命令如何运行外部依赖JAR包》在Java应用部署中,java-jar命令是启动可执行JAR包的标准方式,但当应用需要依赖外部JAR文件时,直接使用java-jar会面临类加载困... 目录引言:外部依赖JAR的必要性一、问题本质:类加载机制的限制1. Java -jar的默认行为2. 类加