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

相关文章

Spring 依赖注入与循环依赖总结

《Spring依赖注入与循环依赖总结》这篇文章给大家介绍Spring依赖注入与循环依赖总结篇,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1. Spring 三级缓存解决循环依赖1. 创建UserService原始对象2. 将原始对象包装成工

深入解析C++ 中std::map内存管理

《深入解析C++中std::map内存管理》文章详解C++std::map内存管理,指出clear()仅删除元素可能不释放底层内存,建议用swap()与空map交换以彻底释放,针对指针类型需手动de... 目录1️、基本清空std::map2️、使用 swap 彻底释放内存3️、map 中存储指针类型的对象

Java使用正则提取字符串中的内容的详细步骤

《Java使用正则提取字符串中的内容的详细步骤》:本文主要介绍Java中使用正则表达式提取字符串内容的方法,通过Pattern和Matcher类实现,涵盖编译正则、查找匹配、分组捕获、数字与邮箱提... 目录1. 基础流程2. 关键方法说明3. 常见场景示例场景1:提取所有数字场景2:提取邮箱地址4. 高级

Spring-DI依赖注入全过程

《Spring-DI依赖注入全过程》SpringDI是核心特性,通过容器管理依赖注入,降低耦合度,实现方式包括组件扫描、构造器/设值/字段注入、自动装配及作用域配置,支持灵活的依赖管理与生命周期控制,... 目录1. 什么是Spring DI?2.Spring如何做的DI3.总结1. 什么是Spring D

C++ STL-string类底层实现过程

《C++STL-string类底层实现过程》本文实现了一个简易的string类,涵盖动态数组存储、深拷贝机制、迭代器支持、容量调整、字符串修改、运算符重载等功能,模拟标准string核心特性,重点强... 目录实现框架一、默认成员函数1.默认构造函数2.构造函数3.拷贝构造函数(重点)4.赋值运算符重载函数

Springboot项目构建时各种依赖详细介绍与依赖关系说明详解

《Springboot项目构建时各种依赖详细介绍与依赖关系说明详解》SpringBoot通过spring-boot-dependencies统一依赖版本管理,spring-boot-starter-w... 目录一、spring-boot-dependencies1.简介2. 内容概览3.核心内容结构4.

C++ vector越界问题的完整解决方案

《C++vector越界问题的完整解决方案》在C++开发中,std::vector作为最常用的动态数组容器,其便捷性与性能优势使其成为处理可变长度数据的首选,然而,数组越界访问始终是威胁程序稳定性的... 目录引言一、vector越界的底层原理与危害1.1 越界访问的本质原因1.2 越界访问的实际危害二、基

Java 中编码与解码的具体实现方法

《Java中编码与解码的具体实现方法》在Java中,字符编码与解码是处理数据的重要组成部分,正确的编码和解码可以确保字符数据在存储、传输、读取时不会出现乱码,本文将详细介绍Java中字符编码与解码的... 目录Java 中编码与解码的实现详解1. 什么是字符编码与解码?1.1 字符编码(Encoding)1

Python 字符串裁切与提取全面且实用的解决方案

《Python字符串裁切与提取全面且实用的解决方案》本文梳理了Python字符串处理方法,涵盖基础切片、split/partition分割、正则匹配及结构化数据解析(如BeautifulSoup、j... 目录python 字符串裁切与提取的完整指南 基础切片方法1. 使用切片操作符[start:end]2

MyBatis的xml中字符串类型判空与非字符串类型判空处理方式(最新整理)

《MyBatis的xml中字符串类型判空与非字符串类型判空处理方式(最新整理)》本文给大家介绍MyBatis的xml中字符串类型判空与非字符串类型判空处理方式,本文给大家介绍的非常详细,对大家的学习或... 目录完整 Hutool 写法版本对比优化为什么status变成Long?为什么 price 没事?怎