0. Why wxString?
都是宽字符串,为什么不直接使用标准库的 std::wstring ?
原因:
- std::wstring 几乎没有编码转换功能 ,wxString 有,包含与我国国标编码的双向转换;
- 习惯 C++标准库的字符串操作接口?wxString 近乎复刻了 std::wstring 的方法;
- 不喜欢 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)的方法,包括:
- 取子串
- 转数值
- 比较、查找、匹配、替换
- 组装
- 拆分
- 取子串
// 取左边的子串(即从下标为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, 表示让函数自行 “猜测”。猜测依据如下:
- 如以 0x 开头,推理为十六进制;
- 如以 0 开头,推理为八进制;
- 其余全当作是十进制数。
- 比较
// 和 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 包括:
- wxTOKEN_STRTOK:仅含非空子串,等同 strtok()
- wxTOKEN_RET_EMPTY: 删除拆分后,尾部的所有空串
- wxTOKEN_RET_EMPTY_ALL: 保留所有折分结果
- wxTOKEN_RET_DELIMS: 和 wxTOKEN_RET_EMPTY,但每个子串后接起作用的分隔符
- 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");
}