wxWidgets源码分析(9) - wxString
wxString
wxString的中文字符支持
中文字符的编码格式如下:
汉字 | GBK | 区位码 | UTF-8 | UTF-16 |
---|---|---|---|---|
中 | D6 D0 | 54 48 | E4 B8 AD | 4E 2D |
文 | CE C4 | 46 36 | E6 96 87 | 65 87 |
不同操作系统的默认内码
Windows系统(默认GBK):41 42 d6 d0 ce c4
Linux系统(默认UTF-8):41 42 e4 b8 ad e6 96 87
Windows
wxStrubg在window系统中使用了UTF-16编码格式。但是在windows系统中,默认的文件的编码格式为GBK格式,考虑到这点,wxString在对赋值取值时,对字符编码进行了从GBK到UTF-16编码的转换,这也就要求源码必须是GBK格式的!!,否则显示出来的就是乱码,当然还有另外一条路,就是使用wxConv类告诉wxString当前的字符编码是什么格式的。
源码文件格式为GBK时的测试,可以发现只有不进行转换的才可以显示。
测试函数:wxString("AB中文",wxConvUTF8)
输出:空
测试函数:wxString("AB中文")
输出:41 42 4E2D 6587
测试函数:_("AB中文")
输出:41 42 4E2D 6587
源码文件格式为UTF-8时的测试,可以发现只有转换的才可以显示,其他的都显示乱码。
测试函数:wxString("AB中文",wxConvUTF8)
输出:41 42 4E2D 6587
测试函数:wxString("AB中文")
输出:41 42 6D93 E15F 6783
测试函数:_("AB中文")
输出:41 42 6D93 E15F 6783
Linux Unicode
编译wxWidgets 3.0 时指定了“--enable-unicode”选项,指定此选项后wxString的内部编码使用的是UTF-16格式进行编码(注:即使不指定--enable-unicode选项,wxWidgets依旧使用unicode格式保存数据,Windows系统中使用UTF-16,Linux&MAC系统中使用UTF-32)
测试函数:wxString("AB中文",wxConvUTF8)
输出:41 42 4E2D 6587
测试函数:wxString("AB中文")
输出:空
测试函数:_("AB中文")
输出:空
(源码文件格式为UTF-8,如果源文件为非UTF-8格式,则都输出错误)
可以看出,Linux系统中文件格式是 UTF-8 格式的,转换后,wxString内部是UTF-32格式的,如果要在Linux中显示中文,则需要使用wxConvUTF8进行转换。
Linux UTF-8
编译wxWidgets 3.0 时指定了“--enable-utf8 --enable-utf8only”选项,这样在wxString内部编码时就直接使用UTF-8格式了,增加 “--enable-utf8only”选项后,则禁止了字符之间的转换,在效率上有大幅的提升,同时编写代码也容易多了:
测试函数:wxString("AB中文",wxConvUTF8)
输出:41 42 4E2D 6587
测试函数:wxString("AB中文")
输出:41 42 4E2D 6587
测试函数:_("AB中文")
输出:41 42 4E2D 6587
(源码文件格式为UTF-8,如果源文件为非UTF-8格式,则都输出错误)
注意到,上面三种格式的数据都能正常输出。
总结
建议后续在写wxWidgets程序时,界面中涉及到的语言全部是使用英语,这样不管文件是GBK编码也好,是UTF-8编码也好,都能够编译、显示正常。
如果需要支持多过语言,建议使用wxWidgets提供的wxLocale解决方案。
wxString与通用字符串的转换
wxString对象的创建
除了wxString的构造函数外,可以通过以下几种方式来生成wxString对象:
静态方法 | 说明 |
---|---|
wxString::FromAscii() | 通过ASCII码的字符串来创建wxString对象,如果字符串中含有大于等于0x80的字符串,wxString会报错,比如wxString str = wxString::FromAscii("ABC中文"); 在运行时如果开启了debug模式就会收到alert信息,同时显示该字符串如果显示出来是乱码; |
wxString::FromUTF8() | 通过UTF-8字符串来创建对象,比如:wxString test = wxString::FromUTF8("\xF0\x90\x8C\x80"); |
构造函数+wxMBConv对象 | wxMBConv 对象默认值为wxConvLibc |
将wxString对象转换为其他类型数据
wxString 提供了多种方法,可以将数据转换为你需要的数据;
c_str()
原型:
wxCStrData wxString::c_str() const;
本函数用于将wxString对象转换为 const char*
或者 const wchar_t*
格式的数据,以下为使用方法:
const char *p = str.c_str(); // 左侧已经注明类型,c_str函数将会输出`const char*`类型
sprintf(abStr, "sprintf = %s\n", (const char *)str.c_str()); // 必须加强转
跟踪代码可以看到wxCStrData
是通过重载强转操作符实现的类型自动转换:
inline const wchar_t* AsWChar() const;
inline const char* AsChar() const;
const unsigned char* AsUnsignedChar() const
{ return (const unsigned char *) AsChar(); }
// 重载转换操作符
operator const wchar_t*() const { return AsWChar(); }
operator const char*() const { return AsChar(); }
operator const unsigned char*() const { return AsUnsignedChar(); }
operator const void*() const { return AsChar(); }
数据的输出通过的是 wxCStrData::AsChar()
函数,内部实现实际上时通过调用wxString的内部函数AsChar
和mb_str
来实现的:
inline const char* wxCStrData::AsChar() const
{
#if wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY
const char * const p = m_str->AsChar(wxConvLibc);
if ( !p )
return "";
#else // !wxUSE_UNICODE || wxUSE_UTF8_LOCALE_ONLY
const char * const p = m_str->mb_str();
#endif // wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY
return p + m_offset;
}
mb_str()
原型:
const wxCharBuffer mb_str (const wxMBConv &conv=wxConvLibc) const;
使用方法与c_str类似:
const char *p = str.c_str();
函数的实现如下,mb_str
调用AsCharBuf
,AsCharBuf
接着调用AsChar
实现转换(AsChar
函数后续有详细分析):
const wxScopedCharBuffer mb_str(const wxMBConv& conv = wxConvLibc) const
{
return AsCharBuf(conv);
}
wxScopedCharBuffer AsCharBuf(const wxMBConv& conv) const
{
...
if ( !AsChar(conv) )
...
}
我们再看下wxScopedCharBuffer
的实现,它也应当同样实现了wxCStrData
函数的操作符强制转换,跟踪代码:
typedef wxScopedCharTypeBuffer<char> wxScopedCharBuffer;
// 继续 wxScopedCharTypeBuffer,可以看到它也实现了操作符重载,转换为模板指定的类型
// 对于 wxScopedCharBuffer 来说就是 const char*
template <typename T>
class wxScopedCharTypeBuffer
{
public:
typedef T CharType;
const CharType *data() const { return m_data->Get(); }
operator const CharType *() const { return data(); }
}
utf8_str()
函数返回UTF-8字符串,可以看到,它是通过调用mb_str(wxMBConvUTF8())
实现了自身的转换,输出的字符串格式为UTF-8编码的。
const wxScopedCharBuffer utf8_str() const { return mb_str(wxMBConvUTF8()); }
ToStdString()
用于将wxString对象转换称std::string对象,可以看到,最终还是通过调用mb_str
函数进行转换。
std::string ToStdString() const
{
wxScopedCharBuffer buf(mb_str());
return std::string(buf.data(), buf.length());
}
wxString::AsChar转换的关键函数
下面针对Unicode模式的代码分析:
const char *wxString::AsChar(const wxMBConv& conv) const
{
#if wxUSE_UNICODE_UTF8
...
#else // wxUSE_UNICODE_WCHAR
// 调用c_str()获取wchar_t*,由于wxString内部就是使用wchar_t*存储的
// 所以这步调用直接获取到内部的buffer
const wchar_t * const strWC = m_impl.c_str();
const size_t lenWC = m_impl.length();
#endif // wxUSE_UNICODE_UTF8/wxUSE_UNICODE_WCHAR
// 调用`conv.FromWChar`将Unicode字符转换为当天系统的MultiByte字符串
const size_t lenMB = conv.FromWChar(NULL, 0, strWC, lenWC);
if ( lenMB == wxCONV_FAILED )
return NULL;
if ( !m_convertedToChar.m_str || lenMB != m_convertedToChar.m_len )
{
if ( !const_cast<wxString *>(this)->m_convertedToChar.Extend(lenMB) )
return NULL;
}
m_convertedToChar.m_str[lenMB] = '\0';
if ( conv.FromWChar(m_convertedToChar.m_str, lenMB,
strWC, lenWC) == wxCONV_FAILED )
return NULL;
return m_convertedToChar.m_str;
}
调用 wxMBConv::FromWChar
进行字符串转换, 跟踪 wxMBConv::FromWChar
的实现,它会继续调用wxMBConv::WC2MB
执行转换,可参考另外一片文章,有描述此函数的实现。
通过上述过程实现了wxString到本地字符串的转换。
字符集转换
正式代码中通过下面的方式调用(wxWidgets-3.0.2):
wxString testStr("abc中文测试");
接着我们看下wxString内部时如何实现的:
调用wxString的构造函数:
// string.h L1241
wxString(const char *psz)
: m_impl(ImplStr(psz)) {}
wxString的构造函数调用ImplStr来初始化wxString的内部变量m_impl,函数定义如下:
static wxScopedWCharBuffer ImplStr(const char* str,
const wxMBConv& conv = wxConvLibc)
{ return ConvertStr(str, npos, conv).data; }
我们先看下第二个参数wxConvLibc的由来,具体实现我们后面再说:
// strconv.h L563
#define WX_DECLARE_GLOBAL_CONV(klass, name) \
extern WXDLLIMPEXP_DATA_BASE(klass*) name##Ptr; \
extern WXDLLIMPEXP_BASE klass* wxGet_##name##Ptr(); \
inline klass& wxGet_##name() \
{ \
if ( !name##Ptr ) \
name##Ptr = wxGet_##name##Ptr(); \
return *name##Ptr; \
}
// 使用WX_DECLARE_GLOBAL_CONV宏预定义转换对象,实际是声明了一个函数
// 和一个指针,实现wxGet_wxConvLibc函数返回这个指针,保证全局唯一
WX_DECLARE_GLOBAL_CONV(wxMBConv, wxConvLibc)
#define wxConvLibc wxGet_wxConvLibc()
接着 ImplStr 会调用 ConvertStr 来进行转换,函数实现如下:
// string.cpp L385
#if wxUSE_UNICODE_WCHAR
wxString::SubstrBufFromMB wxString::ConvertStr(const char *psz, size_t nLength, const wxMBConv& conv)
{
// 调用 conv.cMB2WC 进行转换
wxScopedWCharBuffer wcBuf(conv.cMB2WC(psz, nLength, &wcLen));
}
继续调用 wxMBConv::cMB2WC 进行字符数据的转换,我们跟踪一下源码,此函数会调用 wxMBConv::ToWChar 进行数据转换,然后返回长度:
const wxWCharBuffer
wxMBConv::cMB2WC(const char *inBuff, size_t inLen, size_t *outLen) const
{
const size_t dstLen = ToWChar(NULL, 0, inBuff, inLen);
//...
查看wxMBConv::ToWChar的源码可以看到,它继续调用MB2WC方法进行字符的转换,这个是个虚接口,接着我们看看这个虚接口的实现。
接着上文的 wxConvLib ,我们看看是怎么实现的。
// strconv.cpp L3417
#define WX_DEFINE_GLOBAL_CONV2(klass, impl_klass, name, ctor_args) \
WXDLLIMPEXP_DATA_BASE(klass*) name##Ptr = NULL; \
WXDLLIMPEXP_BASE klass* wxGet_##name##Ptr() \
{ \
static impl_klass name##Obj ctor_args; \
return &name##Obj; \
} \
/* this ensures that all global converter objects are created */ \
/* by the time static initialization is done, i.e. before any */ \
/* thread is launched: */ \
static klass* gs_##name##instance = wxGet_##name##Ptr()
// strconv.cpp L3437
#ifdef __WINDOWS__
WX_DEFINE_GLOBAL_CONV2(wxMBConv, wxMBConv_win32, wxConvLibc, wxEMPTY_PARAMETER_VALUE);
#elif 0 // defined(__WXOSX__)
WX_DEFINE_GLOBAL_CONV2(wxMBConv, wxMBConv_cf, wxConvLibc, (wxFONTENCODING_UTF8));
#else
WX_DEFINE_GLOBAL_CONV2(wxMBConv, wxMBConvLibc, wxConvLibc, wxEMPTY_PARAMETER_VALUE);
#endif
上面的宏展开后,可以得到在windows平台上 wxConvLibc
的实现类是 wxMBConv_win32
,在其他平台上实现类是 wxMBConvLibc
。
对于windows平台中wxMBConv_win32
实现可参考 strconv.cpp 中的实现,主要实现了 MB2WC 和 WC2MB 两个接口,功能说明可以参考wxMBConv类的接口定义:
- MB2WC:用于实现从multibyte encoding 到 Unicode的转换,也就是GBK到UTF-16的转换;具体的实现就是调用win32API MultiByteToWideChar 函数执行字符集的转换
- WC2MB:与上一个相反,用于实现 Unicode 到 multibyte encoding的转换;具体的实现是调用 WideCharToMultiByte 函数执行字符集的转换。
对于Unix平台,实际上时调用的mbstowcs()
和wcstombs()
实现的转换,这两个函数是标准C库函数,这两个函数的实现与当前系统的locale息息相关,手册上明确说明这两个函数的行为依赖 LC_CTYPE 值,至于具体的实现,可以参考locale相关的文档。
wxWidgets源码分析(9) - wxString的更多相关文章
- wxWidgets源码分析(8) - MVC架构
目录 MVC架构 wxDocManager文档管理器 模板类创建文档对象 视图对象的创建 创建顺序 框架菜单命令的执行过程 wxDocParentFrame菜单入口 wxDocManager类的处理 ...
- wxWidgets源码分析(7) - 窗口尺寸
目录 窗口尺寸 概述 窗口Size消息的处理 用户调整Size消息的处理 调整窗口大小 程序调整窗口大小 wxScrolledWindow设置窗口大小 获取TextCtrl控件最合适大小 窗口尺寸 概 ...
- wxWidgets源码分析(5) - 窗口管理
窗口管理 所有的窗口均继承自wxTopLevelWindows: WXDLLIMPEXP_DATA_CORE(wxWindowList) wxTopLevelWindows; wxTopLevelWi ...
- wxWidgets源码分析(4) - 消息处理过程
目录 消息处理过程 消息如何到达wxWidgets Win32消息与wxWidgets消息的转换 菜单消息处理 消息处理链(基于wxEvtHandler) 消息处理链(基于wxWindow) 总结 消 ...
- wxWidgets源码分析(1) - App启动过程
目录 APP启动过程 wxApp入口定义 wxApp实例化准备 wxApp的实例化 wxApp运行 总结 APP启动过程 本文主要介绍wxWidgets应用程序的启动过程,从app.cpp入手. wx ...
- wxWidgets源码分析(6) - 窗口关闭过程
目录 窗口关闭过程 调用流程 关闭文档 删除视图 删除文档对象 关闭Frame App清理 多文档窗口的关闭 多文档父窗口关闭 多文档子窗口关闭 窗口的正式删除 窗口关闭过程总结 如何手工删除view ...
- wxWidgets源码分析(2) - App主循环
目录 APP主循环 MainLoop 消息循环对象的创建 消息循环 消息派发 总结 APP主循环 MainLoop 前面的wxApp的启动代码可以看到,执行完成wxApp::OnInit()函数后,接 ...
- wxWidgets源码分析(3) - 消息映射表
目录 消息映射表 静态消息映射表 静态消息映射表处理过程 动态消息映射表 动态消息映射表处理过程 消息映射表 消息是GUI程序的核心,所有的操作行为均通过消息传递. 静态消息映射表 使用静态Event ...
- ABP源码分析一:整体项目结构及目录
ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...
随机推荐
- poj 2653 线段相交裸题(解题报告)
#include<stdio.h> #include<math.h> const double eps=1e-8; int n; int cmp(double x) { if( ...
- poj 3468A Simple Problem with Integers
Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. On ...
- Codeforces Round #648 (Div. 2) F. Swaps Again
题目链接:F.Swaps Again 题意: 有两个长度为n的数组a和数组b,可以选择k(1<=k<=n/2)交换某一个数组的前缀k和后缀k,可以交换任意次数,看最后是否能使两个数组相等 ...
- Codeforces Round #647 (Div. 2) - Thanks, Algo Muse! C. Johnny and Another Rating Drop (规律,二进制)
题意:有一个正整数\(n\),要求写出所有\(1\)~\(n\)的二进制数,统计相邻的两个二进制同位置上不同数的个数. 题解:打表找规律,不难发现: \(00000\) \(00001\) ...
- ACM International Collegiate Programming Contest, Egyptian Collegiate Programming Contest (ECPC 2015) G. It is all about wisdom (二分,单源最短路)
题意:有\(n\)个点,\(m\)条边,只有当你的智力值大于这条边的\(w\)才能走,问在花费不超过\(k\)的情况下,从\(1\)走到\(n\)的所需的最小智力值. 题解:这题比赛为什么没想出来呢? ...
- Educational Codeforces Round 89 (Rated for Div. 2) B. Shuffle (数学,区间)
题意:有长为\(n\)的排列,其中\(x\)位置上的数为\(1\),其余位置全为\(0\),询问\(m\)次,每次询问一个区间,在这个区间内可以交换任意两个位置上的数,问\(1\)最后出现在不同位置的 ...
- python代理池的构建5——对mongodb数据库里面代理ip检查
上一篇博客地址:python代理池的构建4--mongdb数据库的增删改查 一.对数据库里面代理ip检查(proxy_test.py) #-*-coding:utf-8-*- ''' 目的:检查代理I ...
- 宝塔面板&FLASK¢os 7.2 &腾讯云 配置网站出现的若干问题
1.解决跨域问题&&中文显示问题 from flask import Flask, url_for, request, render_template, redirect from f ...
- IDEA 安装常用操作二
一.IDEA启动慢,因为启动时编译.缓存.创建索引等,如果断电等启动异常,可以让缓存索引失效 迁移IDEAD时,可以找到config.system等文件夹, 随着时间使用越久,空间占用越大,另外觉得浪 ...
- H5 Funny Games All In One
H5 Funny Games All In One H5 游戏 盖楼 游戏 https://iamkun.github.io/tower_game/ https://github.com/iamkun ...