0. 从 std::string::replace 说起
- 代码一
std::string s = "hello world";
std::string t = "abc";
s.replace (0, 5, t, 2);
- 代码二
std::string s = "hello world";
char const* t = "abc";
s.replace (0, 5, t, 2);
以上两段代码,从字面上看,差别仅在 t 的类型,一为 std::string,一为 char const* ,带来二者的运行结果的区别是什么?
感到困惑?你不是第一人,也不会是最后一人。
有没有简单一字符串替换方法,就是那种填入一个原串,一个新串,一执行,就将文本中所有原串,替换成新串的那种?
当然有,pystring 库就是。
- msys2 / ucrt64 子环境下安装指令:
mingw-w64-ucrt-x86_64-pystring; - ubuntu 安装:
sudo apt install libpystring-dev.
1. pystring 的“躺平”
pystring 本意是向 Python 语言学习,思路很好,设计也抄得可以,但在实现上,有点躺平,并且影响了一些功能的正确发挥……所以,在学习它之前,不妨还了解一下它的特点(包括不足)。
2. 上机实践
重点学习以下 pystring 函数:
- strip / lstrip / rstrip : 去除前后缀字符 (不要用于多字节字符文本)
- split : 以指定子串拆分
- splitline :以 \n \r 等主流操作系统使用的换行符拆行 (场景:读文件)
- partition :三段式拆分(场景:解析配置行)
- startswith / endswith : 判断字符串是否以指定子串开始或收尾 (场景:解析协议)
- find / replace :高频使用,简单明了
- count : 简单统计
- join : split 的返操作(场景:日志输出)
3. 代码
3.1 tasks.json
用于 vscode。
注:你需要将 command 、-I 、-L 参数,以及 options下的 cwd 字段中出现路径,分别替换为你的 msys2 相关路径 。
{
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: g++.exe 生成活动文件",
"command": "C:\\msys64\\ucrt64\\bin\\g++.exe",
"args": [
"-fdiagnostics-color=always",
"-g",
"${file}",
"-I", "c:\\msys64\\ucrt64\\include",
"-o",
"${fileDirname}\\${fileBasenameNoExtension}.exe",
"-L", "c:\\msys64\\ucrt64\\lib",
"-lpystring"
],
"options": {
"cwd": "C:\\msys64\\ucrt64\\bin"
},
"problemMatcher": [
"$gcc"
],
"group": "build",
"detail": "调试器生成的任务。"
}
],
"version": "2.0.0"
}
3.2 main.cpp
#include <cassert>
#include <iostream>
#include <vector>
#include <map>
#include <pystring/pystring.h>
// 打印 vector 内的多个字符串
void print_lines(std::vector<std::string> const& lines
, char const* prefix = ""
, char const* suffix = "\n"
)
{
std::cout << "\n";
for (auto const& l : lines)
{
std::cout << prefix << l << suffix;
}
std::cout << "\n";
}
// 去除字符串的前后缀
void demo_strip() // lstrip, rstrip
{
std::string s = "[\"123,456,789\"] ";
std::string chars = "[]\" ";
std::string s2 = pystring::strip(s, chars);
std::cout << s2 << std::endl;
std::string k1 = "一生只做", k2 = "中国人";
std::cout << pystring::lstrip(k1, "一") << "\n"
<< pystring::lstrip(k2, "一") << std::endl;
}
// 拆分字符串
void demo_split()
{
std::string s1 = "苹果,梨,西瓜,葡萄,橙,哈蜜瓜";
auto fruits = pystring::split(s1, ",");
print_lines(fruits);
std::string s2 = "settings={a==b; c > d; a+c<=k}";
auto parts = pystring::split(s2, "=", 1); //1 : 只拆一次
print_lines(parts);
}
// 专用于拆分行
void demo_splitlines()
{
std::string s = "床前明月光,\n疑是地上霜,\r举头望明月,\r\n低头思故乡。\n\r作者:李白";
auto lines = pystring::splitlines(s);
std::cout << lines.size() << "\n";
print_lines(lines, "", "☇");
}
// 三段式拆分
void demo_partition()
{
std::string settings = "1+2+3+4+5+6=(6+1)*3";
auto parts = pystring::partition(settings, "=");
assert(parts.size() == 3);
print_lines(parts);
}
// 将三段式拆分、普通的拆分和去除前后缀的组合
void demo_partition_split_strip()
{
std::string setting = "list =[\"size=1024\", \" 192.8.16.2 \",{12 Tom 1834} ]";
auto parts = pystring::partition(setting, "=");
assert(parts.size() == 3);
auto items = pystring::split(parts[2], ",");
for (auto & item : items)
{
item = pystring::strip(item, "[]\" ");
}
print_lines(items);
}
// 开始于,或收尾于指定的子串
void demo_startswith_endswith()
{
std::string s = "你好,pystring";
if (pystring::startswith(s, "你好") && pystring::endswith(s, "string"))
{
std::cout << "这句话好像是在对一段绳子问好!" << std::endl;
}
}
// 查找和替换
void demo_find_replace()
{
std::vector<std::string> s {"她爱吃萝卜", "她爱胡萝卜"};
for (auto& i : s)
{
if (pystring::find(i, "吃") != -1)
{
i = pystring::replace(i, "萝卜", "白菜");
}
}
print_lines(s);
}
// 子串出现个数
void demo_count()
{
std::string s = "ababaab";
std::vector<std::string> keys = {"ab", "aba", "abc"};
std::map<std::string, int> counts;
for (auto const& k : keys)
{
counts[k] = pystring::count(s, k);
}
for (auto [k, v] : counts)
{
std::cout << k << " 出现了 " << v << " 次!\n";
}
std::cout << std::endl;
}
void demo_join()
{
std::vector<std::string> fruits {"苹果", "山楂", "葡萄", "梨"};
std::cout << "我爱吃:" << pystring::join(",", fruits) << "。\n";
}
int main()
{
std::vector<std::string> words = {"Hello", "World", "from", "pystring::join"};
std::cout << pystring::join(" ~ ", words) << "\n"
<< pystring::rjust("—— 活着最重要,偶尔可躺平", 60) << "\n"
<< pystring::rjust("—— d2school (宣)", 53) << std::endl;
demo_strip();
demo_split();
demo_splitlines();
demo_partition();
demo_partition_split_strip();
demo_startswith_endswith();
demo_find_replace();
demo_count();
demo_join();
}
附:“中”和“一”编码的秘密
汉字 “中” 和 “一” 的 UTF8 编码,分别是:
- 中:E4 B8 AD
- 一:E4 B8 80
于是,代码:
pystring::lstrip("中国人", "一");
在编译器看来,相当于:
pystring::lstrip("\xE4\xB8\xAD\xE5\x9B\xBD\xE4\xBA\xBA", "\xE4\xB8\x80");。