c++使用iconv进行字符编码格式转换

news/2025/3/31 13:44:11/

iconv

在这里插入图片描述
这是一个典型的C风格的函数

std::size_t iconv (iconv_t cd、const char* * inbuf、size_t * inbytesleft、
char* * outbuf、size_t * outbytesleft);

iconv官方文档

使用方法

  1. 打开一个转换句柄
iconv_t iconv_open = iconv_open(const char* tocode, const char* fromcode);

iconv_t 的底层实际类型是void*

iconv_open 可能带来的错误:

  • EINVAL 不支持的编码转换
  • ENOMEM “Out of memory” 内存不足
  1. 执行转换

std::size_t iconv(	iconv cd,char** inbuf, std::size_t * inbytesleft,char** outbuf, std::size_t * outbytesleft);

iconv带来的错误:

  • E2BIG Output buffer too small 输出缓冲区内存不够
  • EILSEQ 输入字符串序列有误
  • EINVAL 输入的字符序列不完整

param
inbuf 参数是一个变量的地址,该变量指向输入序列的第一个字符
inbytesleft 表示该缓冲区中的字节数。
outbuf 参数是一个变量的地址,该变量指向输出缓冲区中可用的第一个字节;
outbytesleft 表示输出缓冲区中可用的字节数。
3. 关闭转换句柄

int iconv_close(iconv_t cd);

转换过程

主要的情况是当inbuf 不为 NULL 且 *inbuf 不为 NULL 时。在这种情况下,iconv() 函数会将从 *inbuf 开始的多字节序列转换为从*outbuf开始的多字节序列。最多会读取从*inbuf 开始的 *inbytesleft 个字节。最多会写入从 *outbuf 开始的 *outbytesleft 个字节。

iconv() 函数一次转换一个多字节字符,对于每个字符转换,它会将 *inbuf 增加,并将 *inbytesleft 减去已转换的输入字节数,将 *outbuf 增加,并将 *outbytesleft 减去已转换的输出字节数,同时更新 cd 中包含的转换状态。如果输入的字符编码是有状态的,iconv() 函数还可以将输入字节序列转换为转换状态的更新而不产生任何输出字节;这种输入称为移位序列。转换可以因五种原因停止:

  • 输入中遇到无效的多字节序列。在本例中,它将errno设置为EILSEQ并返回(size_t) -1。*inbuf左指向无效多字节序列的开头。

  • 遇到一个多字节序列,该序列有效但无法转换为输出的字符编码。这种情况取决于实现和转换描述符。在GNU C库和GNU libiconv中,如果cd创建时没有使用后缀//TRANSLIT//IGNORE,则转换是严格的:有损转换会产生这种情况。如果指定了后缀//TRANSLIT,则在某些情况下可以通过音译避免这种情况。在musl C库中,这种情况不会发生,因为会使用转换为'*'作为回退。在FreeBSD、NetBSD和Solaris的iconv()实现中,这种情况也不会发生,因为它们会使用转换为'?'作为回退。当满足此条件时,iconv()会将errno设置为EILSEQ并返回(size_t) -1*inbuf会指向无法转换的多字节序列的开头。

  • 输入的字节序列已经完全转换,即*inbytesleft已减少到0。在这种情况下,iconv()会返回在此次调用期间执行的非可逆转换的次数。

  • 在输入中遇到了一个不完整的多字节序列,并且输入字节序列在其后终止。在这种情况下,iconv()会将errno设置为EINVAL并返回(size_t) -1*inbuf会指向不完整多字节序列的开头。

  • 输出缓冲区没有足够的空间来容纳下一个转换后的字符。在这种情况下,iconv()会将errno设置为E2BIG并返回(size_t) -1

  • 另一种情况是当inbufNULL*inbufNULL,但outbuf不为NULL*outbuf不为NULL时。在这种情况下,iconv()函数会尝试将cd的转换状态设置为初始状态,并在*outbuf处存储相应的移位序列。从*outbuf开始,最多会写入*outbytesleft字节。如果输出缓冲区没有足够的空间来存储此重置序列,它会将errno设置为E2BIG并返回(size_t) -1。否则,它会根据写入的字节数增加*outbuf并减少*outbytesleft

    第三种情况是当inbufNULL*inbufNULL,且outbufNULL*outbufNULL时。在这种情况下,iconv()函数会将cd的转换状态设置为初始状态。

  • iconv()函数返回在此次调用期间以不可逆方式转换的字符数量;可逆转换不会被计数。如果发生错误,iconv()会返回(size_t) -1,并设置errno以指示具体的错误

注意

在每次调用iconv()的系列操作中,最后一次调用应将inbuf*inbuf设置为NULL,以便刷新任何部分转换的输入。

尽管inbufoutbuf的类型为char **,但这并不意味着它们所指向的对象可以被解释为C字符串或字符数组:字符字节序列的解释由转换函数内部处理。在某些编码中,零字节可能是多字节字符的有效部分。

iconv()的调用者必须确保传递给函数的指针适合访问相应字符集中的字符。这包括在对齐要求严格的平台上确保正确的对齐。

示例代码

转换结构体和函数定义

	/// <summary>/// 转换结果结构体/// </summary>struct IConvResult {std::string        conv_result_str;// 转换成功的结果 使用新编码的字符串int                error_code = 0; // 错误码std::string        error_msg = {NULL}; // 错误信息// 判断是否转换成功bool IsSuccess() const {return error_code == 0;}explicit operator bool() const {return IsSuccess();}bool operator!() const {return !IsSuccess();}bool operator==(int code) const {return error_code == code;}bool operator!=(int code) const {return error_code != code;}const char* c_str() const {return IsSuccess() ? conv_result_str.c_str() : error_msg.data();}};
IConvResult Convert(std::string_view in, const char* fromcode, const char* tocode);

以下是转换函数主体:


UniConv::IConvResult UniConv::Convert(std::string_view in, const char* fromcode, const char* tocode) {// 转换返回的结果IConvResult iconv_result;// 获取 iconv 描述符auto cd = GetIconvDescriptorS(fromcode, tocode);	if (!cd || (cd.get() == reinterpret_cast<iconv_t>(-1))) {iconv_result.error_code = errno;iconv_result.error_msg = GetIconvErrorString(iconv_result.error_code);return iconv_result;}// 输入缓冲区//std::vector<char> in_buffer(in.begin(), in.end());const char* inbuf_ptr = in.data(); // 输入缓存std::size_t inbuf_letf = in.size(); // 输入缓存剩余长度// 输出缓冲区constexpr std::size_t initial_buffer_size = 4096;std::vector<char> out_buffer(initial_buffer_size);std::string converted_result;converted_result.reserve(in.size() * 2); // 预分配空间while (true) {char*       out_ptr  = out_buffer.data();std::size_t out_left = out_buffer.size();// 执行转换std::size_t ret = iconv(cd.get(), &inbuf_ptr, &inbuf_letf, &out_ptr, &out_left);// 写入已转换的数据converted_result.append(out_buffer.data(), out_buffer.size() - out_left);if (static_cast<std::size_t>(-1) == ret) {iconv_result.error_code = errno;iconv_result.error_msg = GetIconvErrorString(iconv_result.error_code);break;}// 动态扩展缓冲区if (out_left < 128 && out_buffer.size() < 1048576) { // 最大1MBout_buffer.resize(out_buffer.size() * 2);continue;}// 检查输入是否处理完毕if (inbuf_letf == 0) {// 刷新转换器的内部状态out_ptr = out_buffer.data();out_left = out_buffer.size();ret = iconv(cd.get(), nullptr, &inbuf_letf, &out_ptr, &out_left);converted_result.append(out_buffer.data(), out_buffer.size() - out_left);if (static_cast<std::size_t>(-1) == ret) {iconv_result.error_code = errno;iconv_result.error_msg = GetIconvErrorString(iconv_result.error_code);}break;}}// 返回转换结果if (iconv_result.error_code == 0) {converted_result.shrink_to_fit();iconv_result.conv_result_str = std::move(converted_result);}return iconv_result;
}
static std::unordered_map<std::string, IconvSharedPtr>                  m_iconvDesscriptorCacheMapS;
using IconvSharedPtr = std::shared_ptr <std::remove_pointer<iconv_t>::type>;
///自定义删除器// 自定义删除器,用于释放 iconv_t 资源struct IconvDeleter {void operator()(iconv_t cd) const {std::cerr << "Closing iconv_t: " << cd << std::endl;// 只有在 cd 不是无效句柄时才调用 iconv_closeif (cd != reinterpret_cast<iconv_t>(-1)) {iconv_close(cd);}}};UniConv::IconvSharedPtr UniConv::GetIconvDescriptorS(const char* fromcode, const char* tocode)
{std::string key = std::string(fromcode) + ":" + tocode;std::lock_guard<std::mutex> lock(m_iconvcCacheMutex);auto it = m_iconvDesscriptorCacheMapS.find(key);if (it != m_iconvDesscriptorCacheMapS.end()) {return it->second; // 返回 shared_ptr 的拷贝}iconv_t cd = iconv_open(tocode, fromcode);if (cd == reinterpret_cast<iconv_t>(-1)) {std::cout << "iconv_open error" << std::endl;return nullptr;}auto iconvPtr = std::shared_ptr<std::remove_pointer_t<iconv_t>>(cd, IconvDeleter());m_iconvDesscriptorCacheMapS.emplace(key, iconvPtr);return iconvPtr;
}

以上便是一个简单的封装


http://www.ppmy.cn/news/1583784.html

相关文章

第十六届蓝桥杯模拟二(串口通信)

由硬件框图可以知道我们要配置LED 和按键 一.LED 先配置LED的八个引脚为GPIO_OutPut,锁存器PD2也是,然后都设置为起始高电平,生成代码时还要去解决引脚冲突问题 二.按键 按键配置,由原理图按键所对引脚要GPIO_Input 生成代码,在文件夹中添加code文件夹,code中添加fun.…

蓝桥与力扣刷题(蓝桥 蓝桥骑士)

题目&#xff1a;小明是蓝桥王国的骑士&#xff0c;他喜欢不断突破自我。 这天蓝桥国王给他安排了 N 个对手&#xff0c;他们的战力值分别为 a1,a2,...,an&#xff0c;且按顺序阻挡在小明的前方。对于这些对手小明可以选择挑战&#xff0c;也可以选择避战。 身为高傲的骑士&a…

人体的三个 Bug

写在前面&#xff1a;​【财富自由计算器】已上线&#xff0c;快算算财富自由要多少​ 我将违背我的天性&#xff0c;忤逆我的本能&#xff0c;永远爱你。——《自私的基因》 最近在看的一些书和课程&#xff0c;都提到了人类生理机制的特点&#xff0c;索性汇总到一块。 我愿…

UE4学习笔记 FPS游戏制作17 让机器人持枪 销毁机器人时也销毁机器人的枪 让机器人射击

添加武器插槽 打开机器人的Idle动画&#xff0c;方便查看武器位置 在动画面板里打开骨骼树&#xff0c;找到右手的武器节点&#xff0c;右键添加一个插槽&#xff0c;重命名为RightWeapon&#xff0c;右键插槽&#xff0c;添加一个预览资产&#xff0c;选择Rifle&#xff0c;根…

智能宠物门禁“黑白颠倒”?快瞳AI用双身份档案破解宠物身份识别难题

现象解读&#xff1a;同一只猫为何被AI“误判”为两只宠物&#xff1f; 在智能宠物门禁场景中&#xff0c;主人常发现&#xff1a;自家猫咪白天能轻松通过门禁&#xff0c;但到了夜晚却“无端被拒”。这并非AI“任性”&#xff0c;而是技术局限与宠物生理特征共同作用的结果&a…

MOSN(Modular Open Smart Network)-08-MOSN 扩展机制解析

前言 大家好&#xff0c;我是老马。 sofastack 其实出来很久了&#xff0c;第一次应该是在 2022 年左右开始关注&#xff0c;但是一直没有深入研究。 最近想学习一下 SOFA 对于生态的设计和思考。 sofaboot 系列 SOFAStack-00-sofa 技术栈概览 MOSN&#xff08;Modular O…

第六届电气、电子信息与通信工程国际学术会议 (EEICE 2025)

重要信息 官网&#xff1a;www.eeice.net&#xff08;点击了解参会投稿等&#xff09; 时间&#xff1a;2025年4月18-20日 地点&#xff1a;中国-深圳技术大学 简介 第六届电气、电子信息与通信工程 (EEICE 2025&#xff09;将于2025年4月18-20日在中国深圳召开。 EEICE 20…

(C语言)文本动态通讯录(动态通讯录升级版)(C语言小项目)

1.首先是头文件&#xff1a; //头文件 //contact.h//防止头文件被重复包含 #pragma once //定义符号常亮&#xff0c;方便维护和修改 //联系人基本信息容量 #define NAME_MAX 20 #define AGE_MAX 5 #define SEX_MAX 5 #define TELE_MAX 15 #define ADDR_MAX 30 //联系人最大容量…