加载中...
基础工具: wxString
第1节:在Windows桌面打个叉
第2节:在窗口上跟踪输出鼠标坐标—Win32版
第3节:你好!wxWidgets
第4节:深入分析基于框架窗口的应用
第5节:玩转主菜单
第6节:简简单单,状态栏
第7节:学工具栏,懂 MVC
第8节:基于对话框的应用
第9节:事件的静态与动态绑定
第10节:实战 Windows屏幕保护
第11节:基础工具: wxString
课文封面
  1. wxString 与 UTF-8 和 GB(国标)编码之间的双向转换
  2. wxString 和 std::wstring 的兼容接口
  3. wxString 更多更好的方法(组装、拆分、转数值……)

0. Why wxString?

都是宽字符串,为什么不直接使用标准库的 std::wstring ?

原因:

  1. std::wstring 几乎没有编码转换功能 ,wxString 有,包含与我国国标编码的双向转换;
  2. 习惯 C++标准库的字符串操作接口?wxString 近乎复刻了 std::wstring 的方法;
  3. 不喜欢 C++标准库的字符串操作接口?wxString 也有更好的。

1. 编码转换

同样用来表达宽字符串,wxString 相比 C++ 标准库所提供的 wstring,最直接的优势就是:既支持与同属 UNICODE 的 UTF-8编码(非等宽编码之一)双向转换,又支持与各国(至少中国)的 GB编码字符双向转换。

1.1 视频1

1.2 相关代码

// GB -> wxString wxString FromGB2312(std::string const& src) { return wxString(src.data(), wxCSConv(L"gb2312")); // wxConvLocal } // wxString -> GB std::string ToGB2312(wxString const& src) { return std::string(src.mb_str(wxCSConv(L"gb2312"))); // wxConvLocal } void ShowResult(wxString name, wxString result) { wxString title = L"测试项目 - " + name; wxMessageBox(result, title); } void testUTF82WX2UTF8() { wxString msg; // 一、UTF8 → wxString std::string utf8Str = "我是一个UTF8编码字符串!"; // 注意:文件编码、无需前缀 wxString fromUTF8 = wxString::FromUTF8(utf8Str.data()); msg << fromUTF8 << L"但其实已经被转换到等宽编码了。" L"\n当前操作系统下,每个字符编码占用:" << sizeof(wxString::char_type) << L"字节"; ShowResult(L"UTF8 -> WX", msg); // 二、wxString → UTF8 auto utf8Str2 = std::string(fromUTF8.ToUTF8()); msg = L"转换前后 UTF8 内容"; if (utf8Str2 == utf8Str) { msg += L"一致!"; } else { msg += L"不一致!但这不可能!!"; } ShowResult(L"WX -> UTF8", msg); } void testGB2WX2GB() { // 三、GB -> wxString wxString fromGB = FromGB2312(thisIsAGBEncodingString); wxString msg = fromGB + L"但其实已经被转换成字符等宽的UNICODE编码了!"; ShowResult(L"GB -> WX", msg); // 四、wxString -> GB std::string gbStr = ToGB2312(fromGB); msg = L"转换前后 GB 编码字符串的内容"; if (gbStr == thisIsAGBEncodingString) { msg += L"一致!"; } else { msg += L"不一致!但这不可能!!"; } ShowResult(L"WX -> GB", msg); } void testNickLength() { std::string utf8Nick = "高傲de火箭"; if (utf8Nick.size() > 6) { wxMessageBox(L"昵称太长,违规!请修改", L"昵称检查", wxOK | wxICON_ERROR); } wxString nick = L"高傲de火箭"; if (nick.size() <= 6) { wxMessageBox(L"昵称长度合规,恭喜!", L"昵称检查"); } }

2. STL 风格的接口

2.1 接口一览

  • 构造
// 实现 std::wstring 到 wxString 的转换 wxString(std::wstring const& src);
  • 赋值
wxString & assign (const wxString &str); wxString & assign (const wxString &str, size_t pos, size_t n); wxString & assign (const wchar_t *sz, size_t n); wxString & assign (const_iterator first, const_iterator last);
  • 追加
wxString & append (const wxString &str); wxString & append (const wxString &str, size_t pos, size_t n); wxString & append (const wchar_t *sz, size_t n); wxString & append (const_iterator first, const_iterator last);
  • 删除
wxString & erase (size_type pos=0, size_type n=npos); iterator erase (iterator first, iterator last); iterator erase (iterator first); void clear (); // 清空
  • 插入
wxString & insert (size_t nPos, const wxString &str); wxString & insert (size_t nPos, const wxString &str, size_t nStart, size_t n); wxString & insert (size_t nPos, const wchar_t *sz, size_t n); void insert (iterator it, const_iterator first, const_iterator last);
  • 替换
wxString& replace(size_t nStart, size_t nLen, const wxString& str, size_t nStart2, size_t nLen2);
  • 查找
size_t find (const wxString &str, size_t nStart=0) const; size_t find (const wchar_t *sz, size_t nStart=0, size_t n=npos) const; size_t find (wxUniChar ch, size_t nStart=0) const; size_t find_first_of (const wchar_t *sz, size_t nStart=0) const; size_t find_first_of (const wchar_t *sz, size_t nStart, size_t n) const; size_t find_last_of (const wxString &str, size_t nStart=npos) const; size_t find_last_of (const wchar_t *sz, size_t nStart=npos) const; size_t find_last_of (const wchar_t *sz, size_t nStart, size_t n) const; size_t find_last_of (wxUniChar c, size_t nStart=npos) const; size_t find_first_not_of (const wxString &str, size_t nStart=0) const; size_t find_first_not_of (const wchar_t *sz, size_t nStart=0) const; size_t find_first_not_of (const wchar_t *sz, size_t nStart, size_t n) const; size_t find_last_not_of (const wxString &str, size_t nStart=npos) const; size_t find_last_not_of (const wchar_t *sz, size_t nStart=npos) const; size_t find_last_not_of (const wchar_t *sz, size_t nStart, size_t n) const; // 反向查找 size_t rfind (const wxString &str, size_t nStart=npos) const size_t rfind (const wchar_t *sz, size_t nStart=npos, size_t n=npos) const
  • 比较
int compare(const wxString& str) const; int compare(size_t nStart, size_t nLen, const wxString& str) const; int compare(size_t nStart, size_t nLen, const wxString& str, size_t nStart2, size_t nLen2) const;

2.2 视频2

2.3 相关代码

// 增、删、改…… void testAssignAppendEraseInsertReplace() { wxString msg; wxString bad = L"别说我不再空虚,我拥有自己的内容,但我还是很悲伤"; msg.assign(bad, 3, 14); ShowResult(L"assign", msg); msg += L"追加"; std::wstring stdWStr = L"我是来自标准库宽字符串的内容"; msg.append(stdWStr, 2, wxString::npos); ShowResult(L"append", msg); msg.erase(2, 4); // 从下标2(包含)开始,往后删除4个字符(2,3,4,5) msg.insert(14, L"合作伙伴"); ShowResult(L"erase/insert", msg); msg.replace(9, 1, L"!"); ShowResult(L"replace", msg); } // find 和 replace 组合 void testFindReplace() { wxString msg = L"我见青山多妩媚,料青山见我应如是"; wxString src = L"青山"; wxString dst = L"大青山和wxWidgets"; for (size_t start = 0;;) { size_t pos = msg.find(src, start); if (pos == wxString::npos) { break; } msg.replace(pos, src.size(), dst); start = pos + dst.size(); } ShowResult(L"find/replace", msg); }

3. 更多,更好的方法

3.1 接口分组

重点讲解五组 wxString 自身(非模仿 STL)的方法,包括:

  1. 取子串
  2. 转数值
  3. 比较、查找、匹配、替换
  4. 组装
  5. 拆分
  • 取子串
// 取左边的子串(即从下标为0开始取),count 指明子串长度 wxString Left (size_t count) const; // 取中间的子串,first 指明子串开始位置,count 指明子串长度 wxString Mid (size_t first, size_t count = wxSTRING_MAXLEN); // 取右边的子串(即从最后一个位置开始取),count 指明子串长度 wxString Right (size_t count) const; // 取串中第一个 ch 字符之后的内容,若查无 ch,返回原串 wxString AfterFirst(wxChar ch) const; // 取串中第一个 ch 字符之前的内容,若查无 ch,返回原串 wxString BeforeFirst(wxChar ch) const; // 查找串中最后一个 ch 字符之后的内容,若查无 ch,返回原串 wxString AfterLast(wxChar ch) const; wxString BeforeLast(wxChar ch) const;
  • 转数值
// 转双精度浮点数 bool wxString::ToDouble(double* val) const; // 以指定进制,转换各种长度的整数 bool ToLong(long* val, int base = 10) const; bool ToLongLong(wxLongLong_t* val, int base = 10) const; bool ToULong(long* val, int base = 10) const; bool ToULongLong(wxLongLong_t* val, int base = 10) const;

如代码所示,第二个参数,进制(base)默认为10, 如字符串所表达的不是十进制数,建议明确指明实际进制(支持从二进制一直到三十六进制),比如 16;或可(但不推荐)指定为 0, 表示让函数自行 “猜测”。猜测依据如下:

  1. 如以 0x 开头,推理为十六进制;
  2. 如以 0 开头,推理为八进制;
  3. 其余全当作是十进制数。
  • 比较
// 和 s 比较内容大小,区分大小写字母(仅ASCII),返回 <0、0、>0 int Cmp(wxString& s) const // 不区分ASCII范围下字母大小写比较 int CmpNoCase(wxString& s) const
  • 查找
// 查找单一字符在源串的首个出现位置,可逆向查找(fromEnd为true时) // 查无结果返回 wxNOT_FOUND int Find(wxChar ch, bool fromEnd = false) const // 查找给定的子串在源串的首个出现位置 或 wxNOT_FOUND int Find(wxChar const* sz) const;
  • 匹配
// 前缀匹配,如提供rest,则该值用于存储前缀之后的内容 bool StartsWith(const wxChar *prefix, wxString *rest = NULL) const // 后缀匹配,如提供rest,则该值用于存储后缀之前的内容 bool EndsWith(const wxChar *suffix, wxString *rest = NULL) const // 使用 '*' and '?' 实现简单的通配判断 bool Matches(const wxChar* szMask) const
  • 替换
// 使用 szNew 替换第一个(或全部)的 szOld 串,返回总共替换的次数 size_t Replace(const wxChar* szOld,  const wxChar* szNew, bool replaceAll = true)
  • 组装

使用 wxString,有三套组装方式可用,以下给出各自示例代码。

wxString season = L"春天", unit = L"月"; // 一、“流”式组装 wxString str1; str1 << L"距离下一个" << season << L"还有 " << 10 << L" 个" << unit; // 二、格式化组装 wxString str2; str2.Format(L"距离下一个%s还有 %d 个%s", season.data(), 10, unit.data()); // 三、加式组装 wxString str3 = L"距离下一个" + season + L"还有 "; str3 += wxString() << 10; str3 += L" 月";
  • 拆分
// 使用 分隔符 拆分字符串(自由函数) wxArrayString wxStringTokenize ( const wxString& str, const wxString& delims = wxDEFAULT_DELIMITERS, wxStringTokenizerMode mode = wxTOKEN_DEFAULT)

其中的拆分方式 wxStringTokenizerMode 包括:

  1. wxTOKEN_STRTOK:仅含非空子串,等同 strtok()
  2. wxTOKEN_RET_EMPTY: 删除拆分后,尾部的所有空串
  3. wxTOKEN_RET_EMPTY_ALL: 保留所有折分结果
  4. wxTOKEN_RET_DELIMS: 和 wxTOKEN_RET_EMPTY,但每个子串后接起作用的分隔符
  5. wxTOKEN_DEFUALT:分隔符仅含空白字符,等同 wxTOKEN_STRTOK,否则等同 wxTOKEN_RET_EMPTY

3.2 视频3

3.3 相关代码

// 测试 wxString 自身的多种求子串的方法 void testWxSubstring() { wxString msg = L"比较大的大提琴"; wxString l = msg.Left(3); wxString m = msg.Mid(2, 2); wxString r = msg.Right(3); auto mrl = m + L" " + r + L" " + l; // 中,右,左 ShowResult(L"左中右 -> 中右左", mrl); auto s1 = msg.BeforeFirst(L'大'); auto s2 = msg.BeforeLast(L''); auto s3 = msg.AfterFirst(L''); auto s4 = msg.BeforeLast(L''); auto newString = s1 + L" " + s2 + L" " + s3 + L" " +s4; ShowResult(L"子串重组", newString); } // 转成数值 void testWxToNumber() { double dv; wxString s = L"3.14159"; s.ToDouble(&dv); ShowResult(L"转换浮点数", wxString(s) << L" == " << dv); long lv; s = L"0x3ECA1234"; s.ToLong(&lv, 16); ShowResult(L"转换16进制整数", wxString(s) << L" == " << lv); // 来个 36进制 s = L"-Z00"; s.ToLong(&lv, 36); ShowResult(L"转换36进制整数", wxString(s) << L" == " << lv); } // 匹配 void testWxMatches() { wxString names [] { L"张三丰", L"章崩溃", L"张家界", L"张天师", L"张三", L"张", L"胡三山" }; wxString zhangAny, zhangXX, three; for (auto s : names) { if (s.Matches(L"张*")) { zhangAny << s << L", "; } if (s.Matches(L"张??")) { zhangXX << s << L", "; } if (s.Matches(L"?三*")) { three << s << L", "; } } wxString msg; msg << L"姓张且名字为两个字:\n" << zhangXX << L"\n\n姓张:\n" << zhangAny << L"\n\n名字包含三:\n" << three; ShowResult(L"Matches", msg); } // wxString 自带的替换方法 void testWxReplace() { wxString msg = L"我见青山多妩媚,料青山见我应如是"; wxString src = L"青山"; wxString dst = L"大青山和wxWidgets"; auto count = msg.Replace(src, dst, true); ShowResult(L"Replace", msg << L"\n(替换发生了 " << count << L" 次)"); } // 拆分 void testWxSplit() { auto const str = L"我是TM的悲伤TM的忧愁的人"; auto r = wxStringTokenize(str, L"TM的"); wxString msg (str); msg << L"\n被 TM的 拆分为:\n"; for (auto t : r) { msg << L"{" << t << L"}\n"; } ShowResult(L"Tokenize", msg); } // 拆分模式 void testWxSplitMode() { wxString const str = L":我:爱!:北京:天安::门:!"; wxString const delims = L":!"; auto splitAndShowResult = [=] (wxStringTokenizerMode mode, wxChar const* modeName) { wxArrayString tokens = wxStringTokenize(str, delims, mode); wxString result = str; result << L"\n使用 " << modeName << L" 模式拆分后,得到:\n"; for (auto t : tokens) { result << L"{" << t << L"}\n"; } ShowResult(L"Tokenize Mode", result); }; splitAndShowResult(wxTOKEN_STRTOK, L"wxTOKEN_STRTOK"); splitAndShowResult(wxTOKEN_RET_EMPTY, L"wxTOKEN_RET_EMPTY"); splitAndShowResult(wxTOKEN_RET_EMPTY_ALL, L"wxTOKEN_RET_EMPTY_ALL"); splitAndShowResult(wxTOKEN_RET_DELIMS, L"wxTOKEN_RET_DELIMS"); splitAndShowResult(wxTOKEN_DEFAULT, L"wxTOKEN_DEFAULT"); }