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的内部函数AsCharmb_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调用AsCharBufAsCharBuf接着调用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类的接口定义:

  1. MB2WC:用于实现从multibyte encoding 到 Unicode的转换,也就是GBK到UTF-16的转换;具体的实现就是调用win32API MultiByteToWideChar 函数执行字符集的转换
  2. WC2MB:与上一个相反,用于实现 Unicode 到 multibyte encoding的转换;具体的实现是调用 WideCharToMultiByte 函数执行字符集的转换。

对于Unix平台,实际上时调用的mbstowcs()wcstombs()实现的转换,这两个函数是标准C库函数,这两个函数的实现与当前系统的locale息息相关,手册上明确说明这两个函数的行为依赖 LC_CTYPE 值,至于具体的实现,可以参考locale相关的文档。

wxWidgets源码分析(9) - wxString的更多相关文章

  1. wxWidgets源码分析(8) - MVC架构

    目录 MVC架构 wxDocManager文档管理器 模板类创建文档对象 视图对象的创建 创建顺序 框架菜单命令的执行过程 wxDocParentFrame菜单入口 wxDocManager类的处理 ...

  2. wxWidgets源码分析(7) - 窗口尺寸

    目录 窗口尺寸 概述 窗口Size消息的处理 用户调整Size消息的处理 调整窗口大小 程序调整窗口大小 wxScrolledWindow设置窗口大小 获取TextCtrl控件最合适大小 窗口尺寸 概 ...

  3. wxWidgets源码分析(5) - 窗口管理

    窗口管理 所有的窗口均继承自wxTopLevelWindows: WXDLLIMPEXP_DATA_CORE(wxWindowList) wxTopLevelWindows; wxTopLevelWi ...

  4. wxWidgets源码分析(4) - 消息处理过程

    目录 消息处理过程 消息如何到达wxWidgets Win32消息与wxWidgets消息的转换 菜单消息处理 消息处理链(基于wxEvtHandler) 消息处理链(基于wxWindow) 总结 消 ...

  5. wxWidgets源码分析(1) - App启动过程

    目录 APP启动过程 wxApp入口定义 wxApp实例化准备 wxApp的实例化 wxApp运行 总结 APP启动过程 本文主要介绍wxWidgets应用程序的启动过程,从app.cpp入手. wx ...

  6. wxWidgets源码分析(6) - 窗口关闭过程

    目录 窗口关闭过程 调用流程 关闭文档 删除视图 删除文档对象 关闭Frame App清理 多文档窗口的关闭 多文档父窗口关闭 多文档子窗口关闭 窗口的正式删除 窗口关闭过程总结 如何手工删除view ...

  7. wxWidgets源码分析(2) - App主循环

    目录 APP主循环 MainLoop 消息循环对象的创建 消息循环 消息派发 总结 APP主循环 MainLoop 前面的wxApp的启动代码可以看到,执行完成wxApp::OnInit()函数后,接 ...

  8. wxWidgets源码分析(3) - 消息映射表

    目录 消息映射表 静态消息映射表 静态消息映射表处理过程 动态消息映射表 动态消息映射表处理过程 消息映射表 消息是GUI程序的核心,所有的操作行为均通过消息传递. 静态消息映射表 使用静态Event ...

  9. ABP源码分析一:整体项目结构及目录

    ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...

随机推荐

  1. <<Hive编程指南>>读书笔记

    1. 设置hive以本地模式运行(即使当前用户是在分布式模式或伪分布式模式下执行也使用这种模式) set hive.exec.model.local.auto=true; 若想默认使用这个配置,可以将 ...

  2. PAT甲级—暴力搜索

    1091 Acute Stroke (30point(s)) 基础的搜索,但是直接用递归会导致段错误,改用队列之后就不会了,这说明递归调用在空间利用率上还是很吃亏的. #include <cst ...

  3. BZOJ1150 [CTSC2007]数据备份Backup 链表+小根堆

    BZOJ1150 [CTSC2007]数据备份Backup 题意: 给定一个长度为\(n\)的数组,要求选\(k\)个数且两两不相邻,问最小值是多少 题解: 做一个小根堆,把所有值放进去,当选择一个值 ...

  4. 分组背包 例题:hdu 1712 ACboy needs your help

    分组背包需求 有N件物品,告诉你这N件物品的重量以及价值,将这些物品划分为K组,每组中的物品互相冲突,最多选一件,求解将哪些物品装入背包可使这些物品的费用综合不超过背包的容量,且价值总和最大. 解题模 ...

  5. windows10上下载远程控制软件之后导致windows10亮度调节功能不能使用

    出现了通用即插即用监视器就可以了

  6. Codeforces Global Round 4 B. WOW Factor (前缀和,数学)

    题意:找出序列中有多少子序列是\(wow\),但是\(w\)只能用\(vv\)来表示. 题解:我们分别记录连续的\(v\)和\(o\)的个数,用\(v1\)和\(v2\)存,这里要注意前导\(o\)不 ...

  7. webpack OSS All In One

    webpack OSS All In One 阿里云 OSS 对象存储(Object Storage Service,简称OSS),是阿里云对外提供的海量.安全和高可靠的云存储服务 https://c ...

  8. WebIDE All In One

    WebIDE All In One web IDE Visual Studio Code vscode Code editing Redefined. Free. Built on open sour ...

  9. Headless Chrome Node API

    puppeteer Headless Chrome Node API https://github.com/GoogleChrome/puppeteer https://pptr.dev/ PWA h ...

  10. git & github & git clone & 'git clone' failed with status 128

    git & github & git clone & 'git clone' failed with status 128 'git clone' failed with st ...