加载中...
pystring-躺平的C++字符串工具库
第1节:libfswatch-文件变动监控
第2节:libiconv-字符集编码转换
第3节:CLI11-命令行参数解析
第4节:nlohmann/json-自然的JSON库
第5节:libb64-理解并玩转base64编码
第6节:libSnappy-快速压缩工具
第7节:spdlog-首选的C++日志库
第8节:libUSB-脱掉USB的外套
第9节:libxlsxwriter-让数据说话
第10节:sqlite-orm-迅速上手.上
第11节:sqlite-orm-迅速上手.下
第12节:libExiv2-读写图像元数据
第13节:webview-让浏览器颤抖的力量
第14节:pystring-躺平的C++字符串工具库
第15节:cpr-自制Deepseek心理陪聊师
第16节:ExprTk-计算的力量
课文封面
  1. 通过实例分析 std::string 方法设计上的两个典型问题;
  2. 介绍 pystring 的设计风格: “躺平”;
  3. 通过实例,演示 pystring 去除前后缀、多种拆分、以及查找替换等方法的简便之处,及个别方法在处理多字节字符时存在的问题

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");