Ch 3: 字符串与文本处理
- 掌握 std::string 的常用操作
- 理解字符串拼接和格式化的差异
- 学会使用正则表达式
- 处理编码问题
3.1 Python str vs C++ std::string
Section titled “3.1 Python str vs C++ std::string”Python 字符串基础
Section titled “Python 字符串基础”Python 的 str 是不可变对象:
# Python - 不可变对象s = "Hello"s[0] = 'h' # TypeError! 字符串不可变
# 常用方法s.upper() # "HELLO"s.lower() # "hello"s.capitalize() # "Hello"s.title() # "Hello"s.strip() # 去除两端空白s.replace("l", "L") # "HeLLo"s.split("l") # ["He", "", "o"]s.find("ll") # 2s.startswith("He") # Trues.endswith("lo") # Truelen(s) # 5"Hello" + " " + "World" # "Hello World"f"{s}!" # "Hello!"C++ std::string
Section titled “C++ std::string”C++ 的 std::string 也是不可变对象,但操作方式不同:
#include <string>
std::string s = "Hello";
// 不可变性s[0] = 'h'; // OK,可以修改字符(string 是mutable的,但内容通常不可变)// 但 string 不是 const,可以修改
// 拼接std::string s2 = s + " World"; // "Hello World"s += "!"; // "Hello!"
// 访问字符char c = s[0]; // 'H'char c2 = s.at(0); // 带边界检查,更安全
// 大小和容量std::size_t len = s.length(); // 5std::size_t cap = s.capacity(); // 通常 > 5bool empty = s.empty();
// 子串std::string sub = s.substr(1, 3); // "ell" (位置1,长度3)
// 查找std::size_t pos = s.find("ll"); // 2std::size_t not_found = s.find("xyz"); // std::string::npos字符串构造方式
Section titled “字符串构造方式”#include <string>
// 多种构造方式std::string s1; // 空字符串std::string s2("Hello"); // C 字符串std::string s3(5, 'a'); // "aaaaa"std::string s4(s3); // 拷贝构造std::string s5 = {"Hello"}; // 初始化列表std::string s6 = std::string("Hello") + " World"; // 移动构造
// 隐式构造void process(const std::string& s);process("Hello"); // C 字符串隐式转为 string3.2 字符串拼接与格式化
Section titled “3.2 字符串拼接与格式化”Python f-string
Section titled “Python f-string”Python 3.6+ 的 f-string 是最方便的格式化方式:
name = "Alice"age = 30height = 1.75
# 基本用法s = f"{name} is {age} years old"
# 格式化选项s = f"PI = {3.14159:.4f}" # "PI = 3.1416"s = f"Hex: {255:#x}" # "Hex: 0xff"s = f"Binary: {42:b}" # "Binary: 101010"s = f"{name:>10}" # 右对齐s = f"{name:<10}" # 左对齐s = f"{name:^10}" # 居中
# 表达式s = f"{2 ** 10}" # "1024"s = f"{len(name)}" # "5"C++20 std::format
Section titled “C++20 std::format”C++20 引入了 Python f-string 的等价物:
#include <format>#include <string>
std::string name = "Alice";int age = 30;double height = 1.75;
// 基本用法std::string s = std::format("{} is {} years old", name, age);
// 格式化选项std::format("PI = {:.4f}", 3.14159); // "PI = 3.1416"std::format("Hex: {:x}", 255); // "Hex: ff"std::format("Binary: {:b}", 42); // "Binary: 101010"std::format("{:>10}", "right"); // " right"std::format("{:<10}", "left"); // "left "std::format("{:^10}", "center"); // " center "
// 数字格式化std::format("{:+d}", 42); // "+42"std::format("{:05d}", 42); // "00042"std::format("{:.2%}", 0.25); // "25.00%"std::format("{:e}", 1234.5678); // "1.234568e+03"
// 输出到流std::cout << std::format("Hello, {}!\n", name);C++ 传统格式化
Section titled “C++ 传统格式化”C++20 之前,没有标准格式化库:
#include <iostream>#include <sstream>#include <iomanip>
// 使用流std::cout << "PI = " << 3.14159 << std::endl;
// stringstreamstd::ostringstream oss;oss << "PI = " << std::fixed << std::setprecision(4) << 3.14159;std::string s = oss.str();
// printf 风格(不类型安全)printf("PI = %.4f\n", 3.14159);3.3 正则表达式
Section titled “3.3 正则表达式”Python re 模块
Section titled “Python re 模块”import re
text = "Date: 2024-01-15, Time: 14:30:00"pattern = r"\d{4}-\d{2}-\d{2}"
# 搜索match = re.search(pattern, text)if match: print(match.group()) # "2024-01-15" print(match.start(), match.end()) # (6, 16)
# 查找所有matches = re.findall(pattern, text)print(matches) # ['2024-01-15']
# 替换result = re.sub(r"(\d{4})-(\d{2})-(\d{2})", r"\3/\2/\1", text)print(result) # "Date: 15/01/2024, Time: 14:30:00"
# 分割parts = re.split(r"[,;:\s]+", "a,b;c:d e")print(parts) # ['a', 'b', 'c', 'd', 'e']
# 编译模式(性能优化)compiled = re.compile(r"\d{4}-\d{2}-\d{2}")C++ std::regex
Section titled “C++ std::regex”C++11 引入了正则表达式库:
#include <regex>#include <string>
std::string text = "Date: 2024-01-15, Time: 14:30:00";std::regex pattern(R"(\d{4})-(\d{2})-(\d{2})");
// 搜索std::smatch match;if (std::regex_search(text, match, pattern)) { std::cout << match.str() << std::endl; // "2024-01-15" std::cout << match[0] << std::endl; // "2024-01-15" std::cout << match[1] << std::endl; // "2024" std::cout << match[2] << std::endl; // "01"}
// 查找所有(C++ 需要迭代)std::sregex_iterator it(text.begin(), text.end(), pattern);std::sregex_iterator end;while (it != end) { std::cout << it->str() << std::endl; ++it;}
// 替换std::string result = std::regex_replace(text, std::regex(R"((\d{4})-(\d{2})-(\d{2}))"), "$3/$2/$1"); // "Date: 15/01/2024, Time: 14:30:00"
// 分割std::string input = "a,b;c:d e";std::regex delim(R"([,;:\s]+)");std::sregex_token_iterator it(input.begin(), input.end(), delim, -1);// -1 表示分割符正则表达式语法对照
Section titled “正则表达式语法对照”| Python | C++ | 说明 |
|---|---|---|
\d | \d | 数字 |
\w | \w | 单词字符 |
\s | \s | 空白 |
. | . | 任意字符 |
* | * | 0或更多 |
+ | + | 1或更多 |
? | ? | 0或1 |
{n} | {n} | 精确 n |
{n,m} | {n,m} | n 到 m |
[abc] | [abc] | 字符类 |
[^abc] | [^abc] | 取反 |
(...) | (...) | 捕获组 |
^ | ^ | 行首 |
$ | $ | 行尾 |
3.4 编码处理
Section titled “3.4 编码处理”Python 编码处理
Section titled “Python 编码处理”Python 3 字符串是 Unicode:
# 编码text = "你好"bytes_utf8 = text.encode("utf-8") # b'\xe4\xb8\xad\xe6\x96\x87'bytes_gbk = text.encode("gbk") # b'\xc4\xe3\xba\xc3'
# 解码decoded_utf8 = bytes_utf8.decode("utf-8") # "你好"decoded_gbk = bytes_gbk.decode("gbk") # "你好"
# 编码检测import chardetdetected = chardet.detect(raw_bytes)print(detected) # {'encoding': 'utf-8', 'confidence': 0.99}
# 处理文件编码with open("file.txt", "r", encoding="utf-8") as f: content = f.read()C++ 编码处理
Section titled “C++ 编码处理”C++ 的编码处理更底层:
#include <string>#include <codecvt>#include <locale>
// UTF-8 字符串std::string utf8_str = u8"你好";
// UTF-16 / UTF-32std::u16string u16_str = u"你好";std::u32string u32_str = U"你好";
// C++20 char8_tstd::u8string u8_str = u8"你好";
// 转换(C++11)std::wstring_convert<std::codecvt_utf8<char16_t>, char16_t> conv16;std::u16string u16 = conv16.from_bytes(utf8_str);std::string back = conv16.to_bytes(u16);
// Windows 下的宽字符#ifdef _WIN32 std::wstring wstr = L"你好"; SetConsoleOutputCP(CP_UTF8);#endif编码转换辅助函数
Section titled “编码转换辅助函数”#include <string>#include <string_view>
// 简单的 UTF-8 长度(不等于字符数)std::size_t utf8_length(std::string_view s) { std::size_t len = 0; for (unsigned char c : s) { if ((c & 0xC0) != 0x80) ++len; // 非延续字节 } return len;}
// 判断是否为 ASCIIbool is_ascii(std::string_view s) { for (unsigned char c : s) { if (c > 127) return false; } return true;}3.5 常用字符串操作速查
Section titled “3.5 常用字符串操作速查”| Python | C++ | 说明 |
|---|---|---|
s.upper() | 手动或 std::transform | 转大写 |
s.lower() | 手动或 std::transform | 转小写 |
s.strip() | std::isspace 手动处理 | 去除空白 |
s.split(",") | std::stringstream + std::getline | 分割 |
",".join(list) | std::accumulate 或循环 | 连接 |
s.find("abc") | s.find("abc") | 查找子串 |
s.replace("a","b") | std::regex_replace 或循环 | 替换 |
f"{x}" | std::format("{}", x) | 格式化 |
s[0] | s[0] 或 s.at(0) | 访问字符 |
len(s) | s.length() 或 s.size() | 长度 |
s.startswith("a") | s.rfind("a", 0) == 0 | 前缀检查 |
s.endswith("a") | s.size() >= 1 && s.back() == 'a' | 后缀检查 |
s.isdigit() | std::all_of(s.begin(), s.end(), ::isdigit) | 数字检查 |
3.6 字符串视图 std::string_view
Section titled “3.6 字符串视图 std::string_view”C++17 引入了 string_view,避免不必要的拷贝:
#include <string_view>
// string_view - 不拥有数据的视图void process(std::string_view sv) { std::cout << sv << std::endl; std::cout << "Length: " << sv.length() << std::endl;}
int main() { std::string s = "Hello, World!"; std::string_view sv = s; // 不拷贝 sv = sv.substr(7, 5); // "World"
process("Hello"); // 直接用字面量 process(s); // 不拷贝,自动转 string_view
return 0;}优势:
// ❌ 低效:创建临时 stringvoid bad(const std::string& s);bad(s.substr(0, 5)); // 拷贝子串
// ✅ 高效:用 string_viewvoid good(std::string_view s);good(s.substr(0, 5)); // 只复制指针和长度std::string操作类似 Python str,但需要包含<string>std::format是 C++20 的格式化方案,类似 Python f-stringstd::regex提供正则表达式支持- 编码处理需要显式转换,C++ 没有内置的编码检测
std::string_view是 C++17 的重要改进,避免不必要的拷贝
下章预告:ch04 容器与数据结构。