一、wxString 简介
wxString
是 wxWidgets 库的一部分,它是一个处理字符串的类。这个类提供了一系列处理Unicode和ASCII字符串的功能,类似于C++的 std::string
。wxString
提供了丰富的方法来管理字符串,如连接,查找,替换等。此外,它还支持格式化字符串,可以用于包含变量的字符串输出,这个特性对于需要将变量数据转换为可显示或可存储的文本格式的应用程序非常有用。
wxString
类代表一个 Unicode 字符字符串,能同时处理宽字符(wchar_t)和传统字符(char)。这一双重特性使其在所有情况下都简单易用,并允许为先前 wxWidgets 版本编写的 ANSI 或 Unicode 代码在 wxWidgets 3.0 的统一 Unicode 构建中正确编译和工作。当使用 wxString
时,其过程大多是透明的,尽管有少数例外情况。这意味着 wxString
类的使用对开发者来说非常方便和直观,不需要过多的考虑编码和字符宽度的问题。
二、wxString API 概览
wxString
类试图同时类似于std::string和std::wstring,可以被视为这两种类的替代品。它提供了几乎所有的这些类的方法,行为与标准C++中的行为完全相同,因此这里不进行详细说明。
除了这些标准方法外,wxString
还增加了处理不同字符串编码之间转换的功能,并且提供了许多额外的辅助功能,如格式化输出函数(Printf()
、Format()
等),大小写转换(MakeUpper()
、Capitalize()
等)以及其他各种函数(Trim()
、StartsWith()
、Matches()
等)。所有这些非标准方法都遵循 wxWidgets
的 “CamelCase” 命名规范。
注意,某些 wxString
方法因为兼容性原因存在多个版本。例如,length()
,Length()
和 Len()
都被提供。通常我们新写的项目,用 length()
就好了。
三、wxString 转换
wxString
类提供了丰富的字符串转换函数,这使得它可以从各种编码的字符串创建,并转换为各种格式的字符串。以下是这些函数的简要总结:
- 创建
wxString
:可以从ASCII字符串、当前地区编码的窄字符串、UTF-8 编码的窄字符串、指定编码的窄字符串,或者标准的std::string
、宽字符串、std::wstring
创建wxString
。 wxString
转换:可以将wxString
转换为ASCII字符串、当前地区编码的字符串、UTF-8 编码的字符串、指定编码的字符串,或者转换为标准的std::string
、宽C字符串、std::wstring
。
要注意,某些转换操作可能会导致数据丢失。例如,如果将 非ASCII字符 转换为 ASCII字符,或者将字符串转换为不支持所有字符的编码,这可能会导致信息丢失。为避免这种情况,最好在可能的情况下始终使用 UTF-8 编码,因为它可以表示所有 Unicode 字符,以避免数据丢失。
另外,如果使用的 wxWidgets
版本将 wxUSE_STL 设置为1,则 wxString
的窄字符串和宽字符串的隐式转换会被禁用,并被替换为 std::string
和 std::wstring
的隐式转换。
四、wxString 陷阱
1. wxString 元素访问
-
访问
wxString
的元素,本质上是对wxString::operator[]()
函数的调用,函数返回的是一个特殊的代理类对象,这个对象允许你对字符串的特定索引位置进行char
或wchar_t
类型的赋值。由于这个原因,这个操作符的返回类型并不是char
或wchar_t
,也不是这些类型的引用,而是一个叫做wxUniCharRef
的特殊类型。由于
wxUniCharRef
不是一个基本类型,所以你不能在switch
语句中直接使用s[n]
。因此,以下代码是错误的:wxString s(...); switch (s[n]) { case 'A': ... break; }
截图——
我们需要使用
s[n].GetValue()
,或者使用显式类型转换,例如static_cast<char>(s[n])
:switch ( s[n].GetValue() ) { ... }
注意,如果指定位置的字符无法表示为当前编码的单个字符,显式类型转换将导致断言失败。因此,如果可能出现非ASCII字符,你可能需要转换为int类型。
-
当使用模板推断或C++11的
auto
关键字时,wxUniCharRef
的返回类型也可能导致问题。例如,如果你使用
auto
关键字获取s[0]
,那么实际上获得的是一个指向字符串中对应字符的引用,而不是该字符的复制。这意味着,改变这个引用会直接修改字符串的内容:wxString s("abc"); auto c = s[0]; c = 'x'; // 更改了 s 字符串 wxASSERT( s == "xbc" );
为避免这种情况,你应该明确指定变量类型,或者明确转换返回值。例如,你可以用
int c = s[0]
,或者auto c = s[0].GetValue()
,这样改变c的值就不会影响原来的字符串了:wxString s("abc"); int c = s[0]; c = 'x'; // 不会修改 s 字符串 wxASSERT( s == "abc" );
wxString s("abc"); auto c = s[0].GetValue(); c = 'x'; // 也不会修改 s 字符串 wxASSERT( s == "abc" );
2. wxString 转C字符串
wxString::c_str()
函数的返回值可以转换为窄字符串(char*)或宽字符串(wchar_t*)。通常情况下,根据你如何使用该结果,编译器会选择正确的类型。但有时,由于二义性,编译器无法进行选择(没错,就是编译失败)。例如,当你尝试把 wxString
传递给可以接受窄或宽字符串参数的函数时,就会产生二义性错误。
void dump_text(const char* text);
void dump_text(const wchar_t* text);
wxString s(...);
dump_text(s); // ERROR: 有歧义
dump_text(s.c_str()); // ERROR: 还是有歧义
截图——
这个时候,你需要明确转换为你需要的类型,或者使用一个不含糊的转换函数(wxString::c_str()
很明显就非常含糊)。例如,你可以使用 static_cast<const char*>(s)
或 s.mb_str()
来显式地获取窄字符串,或者使用 static_cast<const wchar_t*>(s.c_str())
或 s.wc_str()
来获取宽字符串。
void dump_text(const char* text);
void dump_text(const wchar_t* text);
dump_text(static_cast<const char*>(s)); // OK, calls (1)
dump_text(static_cast<const wchar_t*>(s.c_str())); // OK, calls (2)
dump_text(s.mb_str()); // OK, calls (1)
dump_text(s.wc_str()); // OK, calls (2)
dump_text(s.wx_str()); // OK, calls ???(具体取决于构建)
使用以上方式,就可以解决由于 wxString::c_str()
函数的返回值的二义性导致的问题。
3. wxString 与 vararg 函数一起使用
基于C++的规则,这类函数中“可变”的参数的类型并没有明确指定,所以编译器不能自动将 wxString
对象或 wxString::c_str()
的返回对象转换为这些未知的类型。因此,wxString
对象或大多数转换函数的结果都不能作为可变参数传递。例如,像 printf()
这样的函数直接传入 wxString
或其转换结果是行不通的。
以下这些代码是没法通过编译的:
printf("Don't do this: %s", s); // Cannot pass non-trivial object of type 'wxString' to variadic function; expected type from format string was 'char *'
printf("Don't do that: %s", s.c_str()); // Cannot pass non-trivial object of type 'wxCStrData' to variadic function; expected type from format string was 'char *'
printf("Nor even this: %s", s.mb_str()); // Cannot pass non-trivial object of type 'const wxScopedCharBuffer' (aka 'const wxScopedCharTypeBuffer<char>') to variadic function; expected type from format string was 'char *'
wprintf("And even not always this: %s", s.wc_str()); // No matching function for call to 'wprintf'
有图有真相——
解决这个问题的一个方式是明确转换为需要的类型,比如,使用 static_cast<const char*>(s)
或 static_cast<const wchar_t*>(s.wc_str())
等明确转换。当然,根据官方说法,这不是最佳解决方案。这里稍微列出这种方法的代码:
printf("You can do this: %s", static_cast<const char*>(s));
printf("Or this: %s", static_cast<const char*>(s.c_str()));
printf("And this: %s", static_cast<const char*>(s.mb_str()));
wprintf("Or this: %s", static_cast<const wchar_t*>(s.wc_str()));
更好的解决方案是:使用 wxWidgets
提供的函数。如 wxPrintf
。wxPrintf
接受 wxString
对象,wxString::c_str()
调用的结果,以及 char*
和 wchar_t*
字符串。这样就不需要做明确的类型转换了。
wxPrintf("You can do just this: %s", s);
wxPrintf("And this (but it is redundant): %s", s.c_str());
wxPrintf("And this (not using Unicode): %s", s.mb_str());
wxPrintf("And this (always Unicode): %s", s.wc_str());
如果无法使用 wxWidgets
提供的函数,而需要将 wxString
对象传递给非 wxWidgets
的可变参数函数,那么还是乖乖的进行类型转换吧。
五、wxString 性能特性
wxString
内部使用 std::basic_string
来存储其内容(除非编译器不支持或在构建 wxWidgets
时特意禁用了),因此,wxString
继承了 std::basic_string
的很多特性。尤其是,大多数现代实现的 std::basic_string
是线程安全的,并且不使用引用计数(这使得复制大字符串可能会很耗费资源),wxString
也具有同样的特性。
默认情况下,wxString
使用专门针对平台依赖的 wchar_t
类型的 std::basic_string
,这意味着它对ASCII字符串的内存效率不高,特别是在Unix平台下,每个ASCII字符,通常适合在一个字节中表示,却需要使用4字节的wchar_t
来表示。
你可以在构建 wxWidgets
时设置 wxUSE_UNICODE_UTF8
为1,这样,UTF-8 编码的字符串表示形式将存储在专门针对 char
的 std::basic_string
中,即常用的 std::string
。在这种情况下,上述的内存效率问题就不存在了,但是许多 wxString
方法的运行时间性能将发生显著改变,特别是,访问字符串的第 N 个字符的操作变为 O(N) 时间,而不是默认的 O(1) 时间。因此,如果你确实使用这种所谓的 UTF-8 构建,应尽量避免使用索引来访问字符串,而应使用迭代器。例如,使用迭代器遍历字符串在常规(“wchar_t
”)和 UTF-8 构建中都是 O(N) 时间,而在 UTF-8 情况下使用索引就变为 O(N^2) 时间,这意味着简单地检查一个相当长的字符串(比如,有几百万个元素)的每个字符可能需要不合理的长时间。
然而,如果你确实使用迭代器,那么 UTF-8 构建可能比默认构建更好,特别是对于内存受限的嵌入式系统。另外要注意的是,GTK+ 和 DirectFB 内部使用 UTF-8,因此使用此构建不仅可以节省ASCII字符串的内存,而且还可以避免在 wxWidgets
和底层工具包之间进行转换。
六、wxString 各类型函数组的官网教程链接索引(英文,但非常通俗易懂)
成员函数
- 构造函数和赋值运算符
- 长度相关函数(Len(), IsEmpty(), …)
- 字符访问相关函数(GetChar(), SetChar(), …)
- 转换函数(c_str(), ToAscii(), ToStdString(), …)
- 串联函数(Append(), Prepend(), operator+(), operator<<(), …)
- 比较函数(Cmp(), IsSameAs(), StartsWith(), …)
- 子串提取函数(SubString(), Left(), AfterLast(), …)
- 大小写转换函数(Lower(), LowerCase(), MakeUpper(), …)
- 搜索和替换函数(Find(), First(), Replace())
- 转换为数字函数(ToDouble(), ToLongLong(), ToULongLong(), …)
- 格式化和打印函数(Printf(), PrintfV())
- 内存管理函数(Alloc(), Shrink(), Clone(), Clear())
- 杂项(Contains(), IsAscii(), IsNumber(), IsWord(), Trim(), …)
- 迭代器接口函数(begin(), cbegin(), rbegin(), …)
- STL接口函数(append(), at(), erase(), find(), …)
静态函数
【wxWidgets 教程】工具类篇:wxString(七) 至此完毕,欢迎大家指正!还请大家点点赞,给我点动力~~