Ch 8: 字符串和格式化
- 熟练使用
std::string - 理解 C 风格字符串和 C++ 字符串的区别
- 掌握 C++20
std::format格式化 - 学会字符串的常见操作
8.1 Python vs C++ 字符串
Section titled “8.1 Python vs C++ 字符串”Python 字符串回顾
Section titled “Python 字符串回顾”# Python - 字符串是不可变对象,功能丰富s = "Hello"s = 'World's = """多行字符串"""
# f-string 格式化name = "Alice"age = 30s = f"{name} is {age} years old"
# 常用操作s.upper() # "ALICE"s.lower() # "alice"s.startswith("A") # Trues.find("li") # 2s.replace("li", "LI") # "ALIce"s.split(",") # ['a', 'b', 'c']",".join(['a','b']) # "a,b"C++ std::string
Section titled “C++ std::string”#include <string>
// 构造std::string s1 = "Hello";std::string s2{"World"};std::string s3(5, 'a'); // "aaaaa"std::string s4(s1, 1, 3); // "ell"(从位置1取3个字符)std::string s5 = s1 + " " + s2; // "Hello World"8.2 std::string 常用操作
Section titled “8.2 std::string 常用操作”#include <string>
std::string s = "Hello, World!";
// 下标访问(不检查边界)char c = s[0]; // 'H's[0] = 'h'; // 可以修改
// at() 访问(带边界检查)char c2 = s.at(0); // 'h'// s.at(100); // 抛出 std::out_of_range
// 首尾字符char first = s.front(); // 'H'char last = s.back(); // '!'
// C++20char c3 = s.front(); // 'H'std::string s = "Hello";
// 长度std::size_t len = s.length(); // 5std::size_t sz = s.size(); // 同 length()
// 容量(已分配的内存)std::size_t cap = s.capacity(); // 通常 > 5
// 判空bool empty = s.empty(); // false
// 预分配容量(避免多次重新分配)s.reserve(100);std::string s = "Hello";
// 拼接std::string greeting = "Hello" + ", " + "World" + "!";
// 追加s.append(" World"); // "Hello World"s += "!"; // "Hello World!"s.push_back('!'); // "Hello World!!"
// insert 插入s.insert(5, ","); // "Hello, World!"
// 重复std::string repeated(5, 'a'); // "aaaaa"std::string s = "Hello, World!";
// find - 正向查找std::size_t pos = s.find("World"); // 7std::size_t npos = s.find("Python"); // std::string::npos(未找到)
// rfind - 从后向前找std::size_t rpos = s.rfind("o"); // 8
// find_first_of - 找任意字符std::size_t fpos = s.find_first_of("aeiou"); // 1 ('e')
// find_last_of - 找最后一个任意字符std::size_t lpos = s.find_last_of("aeiou"); // 13 ('!')
// find_first_not_of / find_last_not_ofstd::string s = "Hello, World!";
// substr - 取子串std::string sub = s.substr(7, 5); // "World"(从位置7取5个字符)std::string sub2 = s.substr(7); // "World!"(从位置7到末尾)
// C++20 ranges 方式std::string_view sv = s;auto view = sv | std::views::drop(7) | std::views::take(5); // "World"std::string s = "Hello, World!";
// replace - 替换s.replace(7, 5, "C++"); // "Hello, C++!"
// erase - 删除s.erase(5, 7); // "Hello!"
// clear - 清空s.clear(); // ""
// resize - 调整大小s.resize(10, 'x'); // 填充 'x's.resize(5); // 截断std::string a = "apple";std::string b = "banana";
// 比较运算符if (a == b) { /* ... */ }if (a < b) { /* a 在字典序小于 b */ }
// compare - 更详细的比较int cmp = a.compare(b); // 负数 if a < bcmp = a.compare(0, 3, "app"); // 比较 a[0..3) 和 "app"
// 大小写敏感// C++ 没有内置大小写不敏感比较,需要自己实现#include <string>
// 数值转字符串std::string num = std::to_string(42);std::string f = std::to_string(3.14);
// 字符串转数值int i = std::stoi("42");double d = std::stod("3.14");long l = std::stol("12345");
// 带进制转换int hex = std::stoi("FF", nullptr, 16); // 255
// 字符串视图(C++17)std::string_view sv = s; // 不拷贝8.3 C++20 std::format
Section titled “8.3 C++20 std::format”Python f-string 对比
Section titled “Python f-string 对比”# Python f-stringname = "Alice"age = 30pi = 3.14159
s = f"{name} is {age} years old"s = f"PI = {pi:.4f}" # 格式化s = f"Hex: {255:#x}" # 十六进制s = f"Binary: {42:b}" # 二进制C++ std::format
Section titled “C++ std::format”#include <format>#include <string>
int main() { std::string name = "Alice"; int age = 30; double pi = 3.14159; int num = 255;
// 基本格式化(类似 f-string) std::string s = std::format("{} is {} years old", name, age); // "Alice is 30 years old"
// 格式化选项 std::string s2 = std::format("PI = {:.4f}", pi); // "PI = 3.1416"
// 位置参数 std::string s3 = std::format("{1} {0}", "world", "Hello"); // "Hello world"
// 进制转换 std::string hex = std::format("{:x}", num); // "ff" std::string HEX = std::format("{:X}", num); // "FF" std::string bin = std::format("{:b}", 42); // "101010" std::string oct = std::format("{:o}", 64); // "100"
// 带前缀的进制 std::string hexp = std::format("{:#x}", num); // "0xff" std::string binp = std::format("{:#b}", 42); // "0b101010"
return 0;}格式规范详解
Section titled “格式规范详解”| 格式 | 含义 | 示例 |
|---|---|---|
{:d} | 整数 | 42 |
{:x} | 十六进制 | 2a |
{:X} | 十六进制大写 | 2A |
{:b} | 二进制 | 101010 |
{:#x} | 带前缀十六进制 | 0x2a |
{:.2f} | 浮点数精度 | 3.14 |
{:>10} | 右对齐(10宽度) | 42 |
{:<10} | 左对齐 | 42 |
{:^10} | 居中对齐 | 42 |
{:0>5} | 零填充 | 00042 |
{:+d} | 显示正号 | +42 |
{:,} | 千位分隔符 | 1,234,567 |
#include <format>
int main() { // 对齐 std::format("{:>10}", "right") // " right" std::format("{:<10}", "left") // "left " std::format("{:^10}", "center") // " center "
// 填充 std::format("{:0>10}", 42) // "0000000042" std::format("{:x<8}", 255) // "ff000000" std::format("{:*^10}", "hi") // "****hi****"
// 组合 std::format("{:0>10.2f}", 3.14) // "0000003.14"
return 0;}8.4 C 风格字符串
Section titled “8.4 C 风格字符串”// C++ 中仍可使用 C 风格字符串const char* cstr = "Hello"; // const char[6]
// strlen - 长度#include <cstring>std::size_t len = std::strlen(cstr); // 5
// strcmp - 比较int cmp = std::strcmp("apple", "banana"); // 负数 if "apple" < "banana"
// strcpy - 拷贝char dest[20];std::strcpy(dest, "Hello"); // 拷贝到 dest
// strcat - 拼接std::strcat(dest, " World"); // 拼接到 dest
// strstr - 查找子串const char* p = std::strstr("Hello World", "World"); // 指向 "World"C 风格字符串的危险
Section titled “C 风格字符串的危险”// ❌ 危险:缓冲区溢出char buf[10];std::strcpy(buf, "hello world"); // 溢出!未定义行为
// ❌ 危险:未初始化的缓冲区char buf2[20];std::strcpy(buf2, "hello"); // 可能包含垃圾数据
// ✅ 安全做法:用 std::stringstd::string s = "hello world"; // 自动管理内存s += " more"; // 自动扩展转换 C 风格字符串
Section titled “转换 C 风格字符串”std::string s = "Hello";
// c_str() - 返回 const char*const char* cstr = s.c_str(); // "Hello"
// data() - C++17 返回 char*(可用于修改)char* data = s.data(); // C++17,可修改
// copy - 拷贝到外部缓冲区char buf[20];s.copy(buf, s.size(), 0);buf[s.size()] = '\0'; // 记得加终止符8.5 字符串视图 (string_view)
Section titled “8.5 字符串视图 (string_view)”什么是 string_view
Section titled “什么是 string_view”// C++17 - string_view 不拥有字符串数据#include <string_view>
// 创建std::string s = "Hello, World!";std::string_view sv = s; // 不拷贝std::string_view sv2 = "Literal"; // 接受字面量
// 修改视图(不修改原字符串)sv.remove_prefix(7); // "World!"sv.remove_suffix(1); // "World"sv = sv.substr(0, 5); // "World"
// 访问char c = sv[0]; // 'W'std::size_t len = sv.size();bool empty = sv.empty();为什么使用 string_view
Section titled “为什么使用 string_view”#include <string_view>
// 用途1:函数参数 - 不拷贝字符串void process(std::string_view sv) { std::cout << sv << std::endl;}
int main() { std::string s = "Hello"; process(s); // 不拷贝 process("World"); // 接受字面量 process(s.substr(0, 3)); // 接受子串}string_view 的注意事项
Section titled “string_view 的注意事项”// string_view 不拥有数据!原字符串必须保持有效const char* get_name() { std::string name = "Alice"; return name.c_str(); // 危险!函数结束后 name 销毁}
// string_view 用途void parse(std::string_view data) { // data 不拥有数据,调用者确保数据有效}8.6 原始字符串
Section titled “8.6 原始字符串”// 普通字符串 - 需要双反斜杠const char* path = "C:\\Users\\name\\file.txt";
// 正则表达式更难const char* regex = "(\\d{4})-(\\d{2})-(\\d{2})"; // 可读性差// 原始字符串 - 不处理转义const char* path = R"(C:\Users\name\file.txt)";
// 多行字符串const char* multiline = R"( SELECT * FROM users WHERE id = 1)";
// 自定义界定符(避免冲突)const char* code = R"cpp( template<typename T> void process(T value) { // code })cpp";8.7 完整示例
Section titled “8.7 完整示例”#include <iostream>#include <string>#include <format>#include <string_view>#include <cctype>
int main() { // 基本字符串操作 std::string name = "Alice"; std::string greeting = std::format("Hello, {}!", name); std::cout << greeting << std::endl;
// 数值格式化 double pi = 3.141592653; std::cout << std::format("PI = {:.2f}", pi) << std::endl;
// 多进制输出 int num = 255; std::cout << std::format("Hex: {:#x}, Bin: {:b}", num, num) << std::endl;
// string_view 轻量视图 std::string full = "The quick brown fox"; std::string_view view = full; std::string_view word = view.substr(4, 5); // "quick" std::cout << word << std::endl;
// 原始字符串(正则表达式、路径) std::string path = R"(C:\Users\Alice\Documents\file.txt)"; std::cout << path << std::endl;
// 字符串处理 std::string s = " Hello, World! "; std::string trimmed; for (char c : s) { if (!std::isspace(c)) { trimmed += c; } } std::cout << trimmed << std::endl; // "Hello,World!"
return 0;}std::string是 C++ 标准字符串,功能类似 Python 的str+=拼接,find()查找,substr()取子串- C++20
std::format类似 Python 的 f-string,用{}占位 string_view是轻量字符串视图,不拥有数据- 避免 C 风格字符串(
char*),用std::string - 原始字符串
R"(...)"不处理转义,更易读
下章预告:ch09 学习条件语句和分支控制。