C++ – 使用标准库std::use_facet和std::codecvt进行跨平台gbk与utf8字符集转换
1 C++标准库中与字符集转换相关的类
1.1 std::use_local和facet
C++中的std::local每个本地化环境对象至少包含了以下的标准facet,
- std::collate
、std::collate - std::ctype
、 std::ctype - std::codecvt<char, char, std::mbstate_t>
std::codecvt<char16_t, char, std::mbstate_t> - std::codecvt<char32_t, char, std::mbstate_t>
std::codecvt<wchar_t, char, std::mbstate_t> - std::moneypunct
std::moneypunct<char, true> - std::moneypunct
std::moneypunct<wchar_t, true> - std::money_get
、std::money_get - std::money_put
、std::money_put - std::numpunct
、std::numpunct - std::num_get
、std::num_get - std::num_put
、std::num_put - std::time_get
、std::time_get - std::time_put
、std::time_put - std::messages
、std::messages
除了这些标准化的facet之外,我们也可以自定义一个facet对象,上述标准化的facet对象都有自己的特定功能,如果我们要访问一个std::local对象中特定的facet对象则需要使用模板方法std::use_facet
,
template< class Facet >
const Facet& use_facet( const std::locale& loc );
那么使用std::use_facet
的作用是什么呢?使用std::use_facet
主要是把一个facet类实例化成为了一个对象,然后就可以使用这个对象的成员函数。
1.2 std::codecvt
在1.1节中我们可以看到,其实std::codecvt
就是一个标准的facet,其原型如下
template<
class InternT,
class ExternT,
class StateT
> class codecvt;
其中,InternT
表示内部编码,ExternT
表示外部编码,StateT
则是不同转换方式的标志。比如说std::codecvt<wchar_t, char, std::mbstate_t>
,其内部编码就是wchar_t
,而外部编码就是char
,而std::codecvt<wchar_t, char, std::mbstate_t>
就是一个在系统原生宽和窄字符集进行转换的一个facet。
而std::codecvt
有以下两个成员函数:
- out:调用do_out虚函数,而do_out虚函数主要是将字符串从内部编码InternT转为外部编码ExternT
- in:调用do_in虚函数,而do_in虚函数则主要是将字符串从外部编码ExternT转为内部编码InternT
所以我们如果调用std::codecvt<wchar_t, char, std::mbstate_t>
的成员函数out
,则可以将字符串从wchar_t转换为char;调用成员函数in
则可以将字符串从char转化为wchar_t。
1.3 std::wstring_convert
在字符集的转换过程中还会用到std::wstring_convert
,其函数模板如下
template< class Codecvt,
class Elem = wchar_t,
class Wide_alloc = std::allocator<Elem>,
class Byte_alloc = std::allocator<char> >
class wstring_convert;
其函数模板的作用主要是进行窄字符std::string与宽字符std::wstring之间的转换,其有常用的两个成员函数
- from_bytes:将byte string转换为wide string
- to_bytes:将wide string转换为byte string
不过遗憾的是,这个函数在C++11启用,但是在C++17就被弃用了,所以在使用下述代码时应选择正确版本的编译器。
2 使用std::use_local和std::codecvt进行跨平台gbk和utf8字符集转换
在下述代码中,先使用std::use_facet实例化std::codecvt<wchar_t, char, std::mbstate_t>
对象,然后再调用其out
和in
成员函数进行宽窄字符的转换,然后使用std::wstring_convert
完成字符集的转换,示例的代码如下,可以对照代码以及第1节的描述梳理思路
#include <iostream>
#include <string>
#include <vector>
#include <locale>
#include <codecvt>
using namespace std;
std::string gb2312_to_utf8(std::string const& strGb2312)
{
std::vector<wchar_t> buff(strGb2312.size());
#ifdef _MSC_VER
std::locale loc("zh-CN");
#else
std::locale loc("zh_CN.GB18030");
#endif
wchar_t* pwszNext = nullptr;
const char* pszNext = nullptr;
mbstate_t state = {};
int res = std::use_facet<std::codecvt<wchar_t, char, mbstate_t> >
(loc).in(state,
strGb2312.data(), strGb2312.data() + strGb2312.size(), pszNext,
buff.data(), buff.data() + buff.size(), pwszNext);
if (std::codecvt_base::ok == res)
{
std::wstring_convert<std::codecvt_utf8<wchar_t>> cutf8;
return cutf8.to_bytes(std::wstring(buff.data(), pwszNext));
}
return "";
}
std::string utf8_to_gb2312(std::string const& strUtf8)
{
std::wstring_convert<std::codecvt_utf8<wchar_t>> cutf8;
std::wstring wTemp = cutf8.from_bytes(strUtf8);
#ifdef _MSC_VER
std::locale loc("zh-CN");
#else
std::locale loc("zh_CN.GB18030");
#endif
const wchar_t* pwszNext = nullptr;
char* pszNext = nullptr;
mbstate_t state = {};
std::vector<char> buff(wTemp.size() * 2);
int res = std::use_facet<std::codecvt<wchar_t, char, mbstate_t> >
(loc).out(state,
wTemp.data(), wTemp.data() + wTemp.size(), pwszNext,
buff.data(), buff.data() + buff.size(), pszNext);
if (std::codecvt_base::ok == res)
{
return std::string(buff.data(), pszNext);
}
return "";
}
int main()
{
std::string str_utf8 = u8"你好";
std::string str_gbk = utf8_to_gb2312(str_utf8);
std::cout << str_gbk << std::endl;
std::string str_res = gb2312_to_utf8(str_gbk);
std::cout << str_res << std::endl;
return 0;
}
输出结果:
你好
浣犲ソ
参考链接
本文作者:StubbornHuang
版权声明:本文为站长原创文章,如果转载请注明原文链接!
原文标题:C++ – 使用标准库std::use_facet和std::codecvt进行跨平台gbk与utf8字符集转换
原文链接:https://www.stubbornhuang.com/2522/
发布于:2023年02月27日 16:24:40
修改于:2023年06月27日 9:41:01
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
评论
50