Skip to content

Ch 8: 字符串和格式化

  • 熟练使用 std::string
  • 理解 C 风格字符串和 C++ 字符串的区别
  • 掌握 C++20 std::format 格式化
  • 学会字符串的常见操作
# Python - 字符串是不可变对象,功能丰富
s = "Hello"
s = 'World'
s = """多行
字符串"""
# f-string 格式化
name = "Alice"
age = 30
s = f"{name} is {age} years old"
# 常用操作
s.upper() # "ALICE"
s.lower() # "alice"
s.startswith("A") # True
s.find("li") # 2
s.replace("li", "LI") # "ALIce"
s.split(",") # ['a', 'b', 'c']
",".join(['a','b']) # "a,b"
#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"
#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++20
char c3 = s.front(); // 'H'
std::string s = "Hello";
// 长度
std::size_t len = s.length(); // 5
std::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"); // 7
std::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_of
std::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 < b
cmp = 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; // 不拷贝
# Python f-string
name = "Alice"
age = 30
pi = 3.14159
s = f"{name} is {age} years old"
s = f"PI = {pi:.4f}" # 格式化
s = f"Hex: {255:#x}" # 十六进制
s = f"Binary: {42:b}" # 二进制
#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;
}
格式含义示例
{: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;
}
// 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"
// ❌ 危险:缓冲区溢出
char buf[10];
std::strcpy(buf, "hello world"); // 溢出!未定义行为
// ❌ 危险:未初始化的缓冲区
char buf2[20];
std::strcpy(buf2, "hello"); // 可能包含垃圾数据
// ✅ 安全做法:用 std::string
std::string s = "hello world"; // 自动管理内存
s += " more"; // 自动扩展
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'; // 记得加终止符
// 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();
#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 不拥有数据!原字符串必须保持有效
const char* get_name() {
std::string name = "Alice";
return name.c_str(); // 危险!函数结束后 name 销毁
}
// string_view 用途
void parse(std::string_view data) {
// data 不拥有数据,调用者确保数据有效
}
// 普通字符串 - 需要双反斜杠
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";
#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 学习条件语句和分支控制。