字符串的分割、合并与格式化
较难2字符串的分割、合并与格式化——让文字像乐高一样拆拼
你有没有遇到过这样的场景:老师发来一串学生名单,中间用逗号隔开,你要把它拆成一个个名字;或者你想把几个数字拼成“12-34-56”这样的格式;又或者你要把计算出来的价格保留两位小数显示?编程中,字符串的分割、合并与格式化就是专门干这些事的。它们能帮你把长字符串按规则切开(分割)、把多个小块连起来(合并)、还能控制数字或文本的显示样式(格式化)。学会它们,你就能像变魔术一样处理文字数据!
下面我们分三招来学,每一招都配生活例子和代码,最后还有完整的运行示例和避坑指南。
一、字符串分割(Split)——把一根长绳子剪成短截
生活例子:过年奶奶剪彩带,或者你把一串电话号码“138-1234-5678”按“-”切开,得到区号、前四位和后四位。编程中,分割就是把一个字符串按照指定分隔符(比如逗号、空格、破折号)变成字符串列表(数组)。
做法对比
- C++:标准库没有直接的
split函数,你需要自己动手。最常用的方法是:用istringstream(输入字符串流)配合getline按分隔符逐段读取。这种方法简单可靠,适合单字符分隔符。如果分隔符复杂(比如多个字符或正则),可以用std::regex或Boost库。 - Python:内置
str.split(sep)方法,直接返回列表,超级方便。
代码细节
C++ 手动实现 split(基于 istringstream + getline)
#include <iostream>
#include <sstream> // istringstream, ostringstream
#include <string>
#include <vector>
using namespace std;
// 自定义 split 函数:按单个字符分隔
vector<string> split(const string& s, char delim) {
vector<string> tokens; // 存放分割后的每个片段
istringstream iss(s); // 把字符串包装成输入流
string token; // 临时存储读到的片段
while (getline(iss, token, delim)) { // 每次读取到分隔符为止
tokens.push_back(token); // 将片段加入vector
}
return tokens;
}
关键点:getline(iss, token, delim)会从流中读取字符直到遇到delim,把读到的内容存入token,然后丢弃分隔符。循环直到流结束。注意:如果原始字符串以分隔符结尾(如"a,b,"),最后一次getline会读到空字符串,这是正常行为。如果你不希望出现空串,可以在添加前判断token是否为空。
Python 的分割
# 直接使用 split 方法
data = "apple,banana,orange,grape"
fruits = data.split(',') # 返回列表 ['apple', 'banana', 'orange', 'grape']
print(fruits)
常见错误与注意事项
- C++ 忘了包含头文件:
#include <sstream>和#include <string>必须写。 - 分隔符是多个字符:
getline只支持单字符分隔符。若分多个字符(如"**"),需要用其他方法,比如用std::string::find手动查找,或者用正则。 - 空字符串问题:如果字符串以分隔符开头或结尾,
getline会产生空字符串片段。比如",a,b"分割后第一个是空串。需要的话可以手动过滤。 - Python 中 split 的默认行为:
split()不带参数时,会按任意空白字符(空格、制表符、换行等)分割,并且自动去掉开头和结尾的空串。但如果指定分隔符,则不会自动去除。
二、字符串合并(Join)——把积木拼成一串
生活例子:你有三个数字“12”“34”“56”,想用“-”连起来变成“12-34-56”。编程中,合并就是把一个列表(或数组)的所有元素用指定的分隔符拼接成一个字符串。
做法对比
- C++:可以用循环逐个加
+,但效率低(每次拼接都创建新字符串)。推荐用ostringstream(输出字符串流)高效合并,或者用std::accumulate(但性能不如ostringstream)。 - Python:
"分隔符".join(可迭代对象)一行搞定。
代码细节
C++ 手动实现 join(基于 ostringstream)
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
// 自定义 join 函数:用分隔符连接字符串列表
string join(const vector<string>& parts, const string& sep) {
ostringstream oss; // 流式输出,高效拼接
for (size_t i = 0; i < parts.size(); ++i) {
if (i > 0) oss << sep; // 除了第一个元素,前面都加分隔符
oss << parts[i]; // 输出当前元素
}
return oss.str(); // 返回拼接后的字符串
}
为什么用 ostringstream 而不是直接 +?假设有100个字符串,用+拼接时,每次都会生成新字符串,导致大量内存分配。而ostringstream像流水线一样,把内容依次写入缓冲区,最后一次性生成字符串,性能好很多。
Python 的 join
parts = ["12", "34", "56"]
joined = "-".join(parts) # 结果 "12-34-56"
print(joined)
注意:join是字符串的方法,分隔符字符串写在前面。它要求参数是可迭代对象(列表、元组、生成器等),并且元素必须是字符串。如果元素是数字,需要先转成字符串。
常见错误与注意事项
- C++ 中直接把
vector<string>传给join:确保元素是string类型,如果是int,需要先转成字符串(用to_string)。 - Python 中元素类型错误:
"-".join([1,2,3])会报错,因为数字不能自动转字符串。要写成"-".join(map(str, [1,2,3]))。 - 合并时空列表/空向量:C++ 的
join处理空向量时,循环不执行,返回空字符串。Python 的join处理空列表时也返回空字符串,合理。
三、字符串格式化(Format)——给数字穿上漂亮衣服
生活例子:考试成绩要显示“92.50”,或者时间显示“08:05”,又或者商品价格保留两位小数“¥19.90”。格式化就是控制数字或文本的显示方式,比如:补零、设置宽度、对齐、保留小数位数等。
做法对比
- C++:
- C风格:
sprintf(不安全,可能缓冲区溢出,不推荐在新代码中使用)。 - C++风格:用
ostringstream配合<iomanip>中的操控符(setw、setfill、fixed、setprecision、left、right等)。
- C风格:
- Python:
- 旧式:
"%10.2f" % value - 现代:
str.format()或 f-string(最推荐,Python 3.6+)。
- 旧式:
详细控制符解释
C++ 常用操控符(需要#include <iomanip>)
| 操控符 | 作用 | 例子 |
|---|---|---|
setw(n) | 设置下一个输出的字段宽度为n | setw(8) 表示输出至少占8个字符 |
setfill(c) | 设置填充字符(默认空格) | setfill('0') 用0填充空白位置 |
fixed | 浮点数以定点格式显示(即保留小数位数) | fixed << setprecision(2) 保留2位小数 |
setprecision(n) | 设置浮点数精度(小数位数) | setprecision(3) 显示3位小数 |
left / right / internal | 对齐方式(左/右/内部) | left << setw(10) << "hello" 左对齐占10格 |
重要:setw只对紧跟着的一个输出有效,每次都需要重新设置。而fixed和setprecision是持久化的,设置后影响后续所有浮点数输出,直到被改变。
C++ 示例:打造完美格式
#include <iostream>
#include <sstream>
#include <iomanip>
using namespace std;
int main() {
double pi = 3.1415926535;
int num = 42;
// 1. 保留两位小数
ostringstream oss1;
oss1 << fixed << setprecision(2) << pi;
cout << "pi保留2位小数: " << oss1.str() << endl; // 输出 3.14
// 2. 宽度8,不足补0(用于学号、编号)
ostringstream oss2;
oss2 << setw(8) << setfill('0') << num;
cout << "编号(补零): " << oss2.str() << endl; // 输出 00000042
// 3. 左对齐宽度10
ostringstream oss3;
oss3 << left << setw(10) << "Hello" << "!";
cout << "左对齐: " << oss3.str() << endl; // 输出 "Hello !"
// 4. 混合使用:学号4位补零,成绩保留1位小数
ostringstream oss4;
oss4 << "学号: " << setw(4) << setfill('0') << 88;
oss4 << " 成绩: " << fixed << setprecision(1) << 92.36;
cout << oss4.str() << endl; // 输出 "学号: 0088 成绩: 92.4"
}
Python 的格式化(f-string)
pi = 3.1415926535
num = 42
# f-string 方式(最推荐)
print(f"pi保留2位小数: {pi:.2f}") # "3.14"
print(f"编号(补零): {num:08d}") # "00000042"
print(f"左对齐: {'Hello':<10}!") # "Hello !"
print(f"学号: {88:04d} 成绩: {92.36:.1f}") # "学号: 0088 成绩: 92.4"
# 或者用 format 方法
print("pi: {:.2f} num: {:04d}".format(pi, num))
格式说明符格式:{变量名:填充字符?对齐?宽度.精度?类型}
- 对齐:
<左对齐,>右对齐,^居中。 - 类型:
d整数,f浮点数,s字符串等。 - 宽度和精度放在一起:
08d表示宽度8补0,.2f表示2位小数。
常见错误与注意事项
- C++ 中忘记
#include <iomanip>:setw等操控符无法使用。 - C++ 中
setw只生效一次:如果连续输出多个字段需要多次设置,否则后面的字段会使用默认宽度。 - C++ 中浮点数精度设置不持久:
fixed和setprecision是持久化的,但如果后面输出另一个浮点数,可能被影响。建议每次格式化前清空ostringstream并重新设置。 - Python 中格式字符串写错:比如
{num:08d},如果num是浮点数会报错。确保格式符匹配变量类型。 - 缓冲区溢出(C 风格
sprintf):sprintf不检查缓冲区大小,容易写越界,建议用snprintf或C++流代替。
完整可运行示例(C++ 和 Python 都带详细注释)
下面给出一个综合例子,模拟一个学生信息处理场景:按逗号分割学生名字和成绩,然后按格式输出。
C++ 版本
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <iomanip> // setw, setfill, fixed, setprecision
using namespace std;
// 分割函数
vector<string> split(const string& s, char delim) {
vector<string> tokens; // 存放分割结果
istringstream iss(s); // 字符串流
string token; // 每次读到的片段
while (getline(iss, token, delim)) {
if (!token.empty()) { // 避免空字符串(可选)
tokens.push_back(token);
}
}
return tokens;
}
// 合并函数
string join(const vector<string>& parts, const string& sep) {
ostringstream oss; // 高效拼接
for (size_t i = 0; i < parts.size(); ++i) {
if (i > 0) oss << sep;
oss << parts[i];
}
return oss.str();
}
int main() {
// 模拟原始数据:学生姓名和成绩用逗号分隔
string raw_data = "张三,92.5,李四,88.3,王五,76.8";
// 第一步:按逗号分割原始数据
vector<string> fields = split(raw_data, ',');
// 现在 fields = ["张三","92.5","李四","88.3","王五","76.8"]
// 第二步:将姓名和成绩配对,并格式化输出
cout << "学生成绩单(格式化版):" << endl;
for (size_t i = 0; i < fields.size(); i += 2) {
string name = fields[i];
double score = stod(fields[i+1]); // 字符串转double
// 使用ostringstream格式化:姓名左对齐宽度10,成绩保留1位小数且宽度6
ostringstream line;
line << left << setw(10) << name; // 左对齐宽度10
line << " " << right << setw(6) << fixed << setprecision(1) << score;
cout << line.str() << endl;
}
// 第三步:将格式化后的各行合并成一个表格字符串(用换行符分隔)
vector<string> formatted_lines;
for (size_t i = 0; i < fields.size(); i += 2) {
ostringstream line;
line << left << setw(10) << fields[i];
line << " " << right << setw(6) << fixed << setprecision(1) << stod(fields[i+1]);
formatted_lines.push_back(line.str());
}
string table = join(formatted_lines, "\n");
cout << "\n完整表格:\n" << table << endl;
// 额外演示:数字补零(学号)
int student_id = 123;
ostringstream oss_id;
oss_id << setw(6) << setfill('0') << student_id;
cout << "\n学号: " << oss_id.str() << endl; // 输出 000123
return 0;
}
输出:
学生成绩单(格式化版):
张三 92.5
李四 88.3
王五 76.8
完整表格:
张三 92.5
李四 88.3
王五 76.8
学号: 000123
Python 版本
# 原始数据
raw_data = "张三,92.5,李四,88.3,王五,76.8"
# 第一步:分割
fields = raw_data.split(',') # ['张三','92.5','李四','88.3','王五','76.8']
# 第二步:格式化输出
print("学生成绩单(格式化版):")
for i in range(0, len(fields), 2):
name = fields[i]
score = float(fields[i+1])
# f-string 格式化:姓名左对齐宽度10,成绩右对齐宽度6保留1位小数
print(f"{name:<10} {score:>6.1f}")
# 第三步:合并成表格字符串
lines = [f"{name:<10} {float(score):>6.1f}" for name, score in zip(fields[::2], fields[1::2])]
table = "\n".join(lines)
print("\n完整表格:\n" + table)
# 额外演示:学号补零
student_id = 123
print(f"\n学号: {student_id:06d}") # 输出 000123
避坑指南(新手最容易犯的错)
- C++ 分割时处理空字符串:如果输入是"a,,b",
getline会产生一个空字符串。视情况决定是否要过滤掉。 - C++ 分割复杂分隔符:不要用
getline去分割多个字符的分隔符,它会失败。需要改用find+substr循环或正则。 - C++ 合并时不要用
+连接大量字符串:效率低,使用ostringstream。 - Python 中
split和join搞混了:记住split是字符串的方法(str.split),join也是字符串的方法(sep.join(iterable)),不是列表的方法。 - 格式化字符串中的类型不匹配:比如对整数使用
{:02f}会报错,整数应该用{:02d}。 - 忘记包含头文件:C++ 中
#include <sstream>、#include <iomanip>、#include <string>、#include <vector>都得写全。 - C++ 中
setw只影响下一个输出:如果你连续输出多个字段,每个都要单独设setw。
相关指引
- 想要更强大的字符串处理(正则表达式)?可以学习
std::regex(C++11)或 Python 的re模块。 - 想了解 C++ 字符串流的更多用法?搜索“
std::istringstream与std::ostringstream详解”。 - 如果你需要对数字进行格式化输出(如科学计数法、十六进制),C++ 的
<iomanip>还有很多操控符,比如hex、dec、oct、scientific等。 - Python 中 f-string 的完整语法可以参考官方文档:“PEP 498 – Literal String Interpolation”。
掌握了分割、合并、格式化这三招,你就能像搭积木一样任意改造字符串,处理成绩单、日志、配置文件都得心应手。快去试试吧!
例题精讲
在Python中,以下哪个方法用于将字符串按指定分隔符拆分为列表?
在Python中,将列表['a','b','c']合并成字符串'a,b,c'应使用哪个方法?
在Python中,字符串格式化可以使用f-string,例如f"Hello {name}"。
给定字符串s = "apple,banana,cherry",将其按逗号分割成列表,代码:result = ___将列表words = ["Hello", "World"]用空格连接成字符串,代码:result = ___