【字符编码转换】使用iconv
一、配置
配置iconv在windows平台会比较麻烦,下面这个是使用vs2022编译的,可以直接使用,其他vs版本自求多福。
https://github.com/pffang/libiconv-for-Windows
引入头文件<iconv.h>和lib文件即可。
官网:libiconv - GNU Project - Free Software Foundation (FSF)
二、使用
使用倒比较简单,只有三个函数:
iconv_open | 设置要转换的编码和转为的编码 |
iconv | 转换操作 |
iconv_close | 资源释放 |
1.iconv_open
iconv_t cd = iconv_open(to, from);
if ((iconv_t)(-1) == cd)
{assert(0 && "iconv_open失败!");// 会递归调用自己,不能使用debugreturn {};
}
函数返回一个句柄iconv_t,如果返回-1代表失败。出现错误可能是因为to、from设置的编码不支持,但是这里不能使用debug输出日志,因为我在debug函数将utf8编码转为本地编码使用了此函数,就发生了递归调用从而栈溢出。读者可以自行考虑怎么处理错误。
2.iconv_close
//资源释放
finally f([&]() {iconv_close(cd);
});//.h
/*** @brief RAII清理操作
*/
template<typename F>
class finally
{
public:finally(F&& func) : _func(func) {}~finally() { _func(); }
private:F _func;
};
上面这种写法无论函数在哪里return,只要脱离f的作用域,iconv句柄就会被调用iconv_close从而释放。
3.iconv
char* in_buf = (char*)buf; //输入指针
size_t in_size = buf_size; //输入大小,直到0停止转换
char* out_buf = temp; //输出缓冲区指针
size_t out_size = SIZE_BUFFER_STRING_CONVERT;//输出缓冲区大小if (-1 == iconv(cd, &in_buf, &in_size, &out_buf, &out_size))
{
}
这里需要句柄cd加上4个变量给予iconv函数,后面4个参数均是取地址,iconv函数会改变它们的值。
in_size我们设为要转换字符串(输入缓冲区in_buf)的大小(字节单位),当完全转换时,in_size就会变为0。那么初始大小buf_size减去in_size就是当前已经转换了的字符数量。
同理输出缓冲区大小SIZE_BUFFER_STRING_CONVERT减去out_size,就是单次调用iconv转换产生的输出大小(字节单位)。
char temp[SIZE_BUFFER_STRING_CONVERT];//缓冲区
string ret;//输出
之所以设置一个固定的输出缓冲区大小,是因为当要转换的字符串过大时,就很难一次转换完成,只能先转换一部分复制到最终的输出(string ret),然后再转换再添加到输出字符串。
所以实际代码是一个循环,返回-1并不是真的失败,当errno为E2BIG时是输出缓冲区不够用了:
do
{//直到转换完if (-1 == iconv(cd, &in_buf, &in_size, &out_buf, &out_size)){if (errno != E2BIG){assert(0 && "转换编码失败!");return {};}else{size_t num = SIZE_BUFFER_STRING_CONVERT - out_size;ret += string(temp, num);//没有转换完,重设输出位置out_size = SIZE_BUFFER_STRING_CONVERT;out_buf = temp;}}else{//完整转换,增加numassert(in_size == 0);size_t num = SIZE_BUFFER_STRING_CONVERT - out_size;if (ret.empty())//第一次就结束了,优化直接returnreturn string(temp, num);ret += string(temp, num);}
} while (in_size);
三、完整代码
整个关键的封装函数即是CvtString,代码如下:
string String::CvtString(const char* from, const char* to, const char* buf, size_t buf_size)
{//前置条件检查if (buf_size == 0)return {};iconv_t cd = iconv_open(to, from);if ((iconv_t)(-1) == cd){assert(0 && "iconv_open失败!");//TODO 会递归调用自己,不能使用debug,改为不使用编码转换的debugreturn {};}//资源释放finally f([&]() {iconv_close(cd);});char temp[SIZE_BUFFER_STRING_CONVERT];//缓冲区string ret;//输出char* in_buf = (char*)buf; //输入指针size_t in_size = buf_size; //输入大小,直到0停止转换char* out_buf = temp; //输出缓冲区指针size_t out_size = SIZE_BUFFER_STRING_CONVERT;//输出缓冲区大小do{//直到转换完if (-1 == iconv(cd, &in_buf, &in_size, &out_buf, &out_size)){if (errno != E2BIG){assert(0 && "转换编码失败!");return {};}else{size_t num = SIZE_BUFFER_STRING_CONVERT - out_size;ret += string(temp, num);//没有转换完,重设输出位置out_size = SIZE_BUFFER_STRING_CONVERT;out_buf = temp;}}else{//完整转换,增加numassert(in_size == 0);size_t num = SIZE_BUFFER_STRING_CONVERT - out_size;if (ret.empty())//第一次就结束了,优化直接returnreturn string(temp, num);ret += string(temp, num);}} while (in_size);return ret;
}
四、使用实例
要实现任意多字节本地编码转为utf8编码,以下调用即可:
string String::cvt_mb_u8(const char* str, const string& code_name)
{return CvtString(code_name.c_str(), "UTF-8", str, strlen(str));
}
code_name为空表示本地系统编码,也可以填入具体的编码,例如"GBK"。可以参考官网给出的全部支持的编码名字:
ICONV_OPEN
如果涉及到wstring宽字符转换,则需要转换一下,因为wchar_t在不同平台大小不一样,所以使用sizeof:
std::wstring String::cvt_mb_wc(const char* str, const string& code_name)
{string out = CvtString(code_name.c_str(), "wchar_t", str, strlen(str));assert(out.size() % sizeof(wchar_t) == 0);//必须是sizeof(wchar_t)的倍数std::wstring ret((wchar_t*)out.c_str(), out.size() / sizeof(wchar_t));return ret;
}
同理基于4字节的char32_t如此:
std::u32string String::cvt_u8_utf(const string& str)
{string out = CvtString("UTF-8", "UCS-4-INTERNAL", str.c_str(), str.size());assert(out.size() % sizeof(char32_t) == 0);//必须是sizeof(char32_t)的倍数std::u32string ret((char32_t*)out.c_str(), out.size() / sizeof(char32_t));return ret;
}//反过来则这样转换
string String::cvt_utf_u8(const utf_string& str)
{return CvtString("UCS-4-INTERNAL", "UTF-8", (char*)str.c_str(), str.size() * sizeof(wchar_t));
}
完整代码可以参考我的项目的DND.String.ixx文件:
DND3D: 基于C++20与标准库的工具集
对你有帮助请点个赞、收藏或关注。
【字符编码转换】使用iconv相关推荐
- iconv 判断字符编码_iconv字符编码转换全攻略
iconv(http://www.gnu.org/software/libiconv/)是一个开源的字符编码转换库,可以"方便"的完成几乎所有的编码转换工作.说简单是因为,它常用的 ...
- c语言使用iconv函数实现字符编码转换
c语言使用iconv函数实现字符编码转换 linux下提供了iconv库来实现字符编码转换,先介绍下命令行: iconv [-f encoding] [-t encoding] [inputfile ...
- android 使用icon进行字符编码转换
在使用ndk开发应用程序时,有时需要字符编码转换,这里使用开源库icon进行字符编码转换,代码如下 char * convertString(const char * fromCode, const ...
- 《MySQL tips:隐式类型转换与隐式字符编码转换对查询效率的影响》
维护一个交易系统,交易记录表tradelog包含交易流水号(tradeid).交易员id(operator).交易时间(t_modified)等字段. create table 'tradelog' ...
- 字符编码转换类(支持多国语言)
头文件StrConvertor.h /* * 字符编码转换库,支持多国语言. */ #pragma once #include <string>class CStrConvertor { ...
- Android字符编码转换,GBK转UTF-8
Android字符编码转换,GBK转UTF-8 网上看了很多都不能用,最后看到这个方法,很靠谱,分享给大家! String str; str = new String(str.getBytes(&qu ...
- Qt中的字符编码转换:UTF8、Unicode、GBK、ASCII、16进制字符、16进制数值
文章目录 前言 简述 ASCII GBK Unicode UTF-8 应用场景 开发环境 编码转换 16进制数值转换为16进制字符 16进制数值转化为字符串 16进制字符串转换为Unicode字符串 ...
- iconv()和mb_conver_encoding()字符编码转换函数
2019独角兽企业重金招聘Python工程师标准>>> 一. `string iconv ( string $in_charset , string $out_charset , s ...
- php iconv lanti1,字符编码转换iconv
iconv命令可以将一种已知的字符集文件转换成另一种已知的字符集文件.它的作用是在多种国际编码格式之间进行文本内码的转换.iconv基于GPL公开源代码,是GNU项目的一部分. 官网地址 附件是Win ...
- linux下字符编码转换
2019独角兽企业重金招聘Python工程师标准>>> 关于字符编码: 字符编码笔记:ASCII,Unicode和UTF-8: http://www.ruanyifeng.com/b ...
最新文章
- debian 7上安装svn
- Android FFmpeg系列——5 音视频同步播放
- 趣链 BitXHub跨链平台 (7)应用链插件
- JSP简单练习-包装类综合应用实例
- 解决element-ui table show-summary合计行不显示问题
- android studio break,Android Studio IDE: Break on Exception
- linux启动清除指定内存,柴少鹏的官方网站
- caffe中 softmax 函数的前向传播和反向传播
- 如何制作BAT(Windows批处理文件)病毒
- UC浏览器去广告、联网、升级(支持新版8.1)
- 关于Elasticsearch的精确值查找(term)不生效问题
- mac 版本charles安装报错-Charles cannot configure your proxy settings while it is on a read-only volume.
- 深入浅出ERC777合约
- 4月书讯 | 一大波好书来袭,最美华章四月天
- SQL注入漏洞-SQL注入原理与实践
- Flask项目之手机端租房网站的实战开发(一)
- 放开后经济会变好吗?越南是怎样度过的?
- 手机端页面 自适应解决方案-收集
- STM32学习笔记(13)——模数转换ADC
- 最容易上手,也最有用的炒股绝招-3年翻N倍!!
热门文章
- Oracle怎样创建共享文件夹,OracleVirtualBox虚拟机如何实现文件夹共享
- 锡兰1.1.0现已上市
- 儿童汽车拼图游戏 - 儿童游戏拼图2岁-5岁
- excel双击后公式计算机,#电脑上的excel表格里的数字为什么要双击才能展开#excel文本双击后变数字...
- java 插件 地图_[Java教程]插件~使用ECharts动态在地图上标识点
- 大主宰PHP文章,大主宰:沈苍生让李玄通放弃洛璃?牧尘受到眷顾!北溟再出手相助...
- 禁止Unity3D中的物体碰撞后旋转
- 一、ODI教程--ODI的介绍
- mc无可用java_新人求助,MC无法打开,内存java均无问题
- Saas 多租户模式介绍