【wxWidgets 教程】工具类篇:wxString(七)

参考文档:
https://docs.wxwidgets.org/3.2/classwx_string.html

一、wxString 简介

wxString 是 wxWidgets 库的一部分,它是一个处理字符串的类。这个类提供了一系列处理Unicode和ASCII字符串的功能,类似于C++的 std::stringwxString 提供了丰富的方法来管理字符串,如连接,查找,替换等。此外,它还支持格式化字符串,可以用于包含变量的字符串输出,这个特性对于需要将变量数据转换为可显示或可存储的文本格式的应用程序非常有用。

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 类提供了丰富的字符串转换函数,这使得它可以从各种编码的字符串创建,并转换为各种格式的字符串。以下是这些函数的简要总结:

  1. 创建 wxString:可以从ASCII字符串、当前地区编码的窄字符串、UTF-8 编码的窄字符串、指定编码的窄字符串,或者标准的 std::string、宽字符串、std::wstring 创建 wxString
  2. wxString 转换:可以将 wxString 转换为ASCII字符串、当前地区编码的字符串、UTF-8 编码的字符串、指定编码的字符串,或者转换为标准的 std::string、宽C字符串、std::wstring

要注意,某些转换操作可能会导致数据丢失。例如,如果将 非ASCII字符 转换为 ASCII字符,或者将字符串转换为不支持所有字符的编码,这可能会导致信息丢失。为避免这种情况,最好在可能的情况下始终使用 UTF-8 编码,因为它可以表示所有 Unicode 字符,以避免数据丢失。

另外,如果使用的 wxWidgets 版本将 wxUSE_STL 设置为1,则 wxString 的窄字符串和宽字符串的隐式转换会被禁用,并被替换为 std::stringstd::wstring 的隐式转换。

四、wxString 陷阱

1. wxString 元素访问

  • 访问 wxString 的元素,本质上是对 wxString::operator[]() 函数的调用,函数返回的是一个特殊的代理类对象,这个对象允许你对字符串的特定索引位置进行 charwchar_t 类型的赋值。由于这个原因,这个操作符的返回类型并不是 charwchar_t,也不是这些类型的引用,而是一个叫做 wxUniCharRef 的特殊类型。

    由于 wxUniCharRef 不是一个基本类型,所以你不能在 switch 语句中直接使用 s[n]。因此,以下代码是错误的:

    wxString s(...);
    switch (s[n]) {
        case 'A':
            ...
            break;
    }
    

    截图——
    switch的编译错误

    我们需要使用 s[n].GetValue(),或者使用显式类型转换,例如 static_cast<char>(s[n])

    switch ( s[n].GetValue() ) {
        ...
    }
    

    注意,如果指定位置的字符无法表示为当前编码的单个字符,显式类型转换将导致断言失败。因此,如果可能出现非ASCII字符,你可能需要转换为int类型。

  • 当使用模板推断或C++11auto 关键字时,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: 还是有歧义

截图——
dump_text 调用有歧义

这个时候,你需要明确转换为你需要的类型,或者使用一个不含糊的转换函数(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 提供的函数。如 wxPrintfwxPrintf 接受 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 编码的字符串表示形式将存储在专门针对 charstd::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 各类型函数组的官网教程链接索引(英文,但非常通俗易懂)

成员函数

静态函数


【wxWidgets 教程】工具类篇:wxString(七) 至此完毕,欢迎大家指正!还请大家点点赞,给我点动力~~

上一篇:【wxWidgets 教程】事件篇Ⅳ(六)
下一篇:【wxWidgets 教程】工具类篇:容器(八)

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
wxWidgets教程是一系列文章或资源,旨在帮助读者了解和学习如何使用wxWidgets构建跨平台的GUI应用程序。这些教程可以包括基本知识、准备工作、创建第一个wxWidgets程序、控件的使用以及其他相关主题。通过阅读这些教程,读者可以逐步了解和掌握wxWidgets的功能和用法。一些常见的wxWidgets教程资源包括官方文档、书籍和在线教程。在官方文档中,您可以找到关于wxWidgets的详细说明和使用指南。而书籍则提供了更深入的介绍和实例,帮助读者更好地理解和应用wxWidgets。此外,还有一些在线教程和博客文章,提供了一些实用的技巧和经验分享。引用<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [wxWidgets完整教程专栏完整目录 cpp](https://blog.csdn.net/update7/article/details/130023533)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [wxWidgets教程(中文)](https://download.csdn.net/download/sweetbo/4871081)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Xiao_Ley

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值