字符串处理
切片、f-string、split/join。
字符串处理
这一课你将能做到
- 熟练使用切片、f-string 完成字符串的截取与格式化,写出对齐、保留小数等常见输出
- 熟练使用 split / join / strip / replace 完成文本的拆分、拼接、清洗
- 说出字符串「不可变」带来的行为规则,解释为什么所有「修改」方法都返回新字符串
为什么要学
脚本世界里 80% 的数据进门时是字符串:CSV 每行是字符串、命令行参数是字符串、网页抓下来的内容是字符串、大模型返回的也是字符串。处理文本的能力直接决定你写脚本的战斗力。而且字符串操作是面试笔试的常客——反转、回文、统计字符频次,全靠这一课的基本功。
人话:字符串像一串穿好的珠子,每颗珠子是一个字符,编号从 0 开始。你不能换掉某颗珠子(不可变),但可以随时「重新穿一串」——所有修改方法其实是给你一串新的。
先建立图像
s = "Python"
下标: 0 1 2 3 4 5
字符: P y t h o n
负下标:-6-5-4-3-2-1
s[0] → 'P' 第一个
s[-1] → 'n' 最后一个
s[0:3] → 'Pyt' 含头不含尾
s[::-1] → 'nohtyP' 反转
f-string 格式化速查:
f"{value:格式}" → 冒号后面是格式说明
:.2f 保留 2 位小数
:>10 右对齐占 10 格 :<10 左对齐 :^10 居中
:, 千分位逗号
:% 百分比
跟着做
步骤 1:切片六连
新建 string_demo.py:
s = "Python"
print(s[0]) # P
print(s[-1]) # n
print(s[1:4]) # yth 含头不含尾
print(s[:2]) # Py 前 2 个
print(s[-2:]) # on 后 2 个
print(s[::-1]) # nohtyP 步长 -1 即反转
# 切片越界不报错(对比下标越界会报 IndexError)
print(s[2:100]) # thon
输出:
P
n
yth
Py
on
nohtyP
thon
切片是字符串最重要的操作,语法 [start:stop:step] 与列表完全一致——学一次两处用。
步骤 2:f-string 格式化全场景
name, price, qty = "机械键盘", 299.5, 3
# 基本插值:变量直接塞进大括号
print(f"{name} 单价 {price} 元")
# 表达式也能放进去
print(f"总价:{price * qty} 元")
# 保留 2 位小数
print(f"总价:{price * qty:.2f} 元")
# 千分位
big = 1234567.891
print(f"{big:,.2f}") # 1,234,567.89
# 对齐:宽度 10
print(f"|{'id':<10}|{'name':^10}|{'price':>10}|")
print(f"|{1:<10}|{name:^10}|{price:>10.1f}|")
# 百分比
ratio = 0.875
print(f"完成度 {ratio:.1%}") # 完成度 87.5%
# 调试神器:= 号同时打印变量名和值
x = 42
print(f"{x=}") # x=42
输出:
机械键盘 单价 299.5 元
总价:898.5 元
总价:898.50 元
1,234,567.89
|id | name | price|
|1 | 机械键盘 | 299.5|
完成度 87.5%
x=42
f-string 是 3.6+ 的标准做法,% 格式化和 .format() 只在维护老代码时认识即可。
步骤 3:不可变——所有方法都返回新串
s = " Hello World "
# strip 去两端空白(含换行、制表符)
print(repr(s.strip())) # 'Hello World'
# lower / upper / replace 全都返回新串
print(s.strip().lower()) # hello world
print(s.replace("World", "Python")) # ' Hello Python '
# 原字符串纹丝不动
print(repr(s)) # ' Hello World '
# 所以必须接住返回值!
s = s.strip() # 重新绑定才生效
print(repr(s)) # 'Hello World'
'Hello World'
hello world
Hello Python
' Hello World '
'Hello World'
这是字符串第一坑:s.strip() 单独写一行,等于什么都没干。字符串方法和列表的 .sort()(原地改)行为模式不同——字符串一律「返回新的」。
步骤 4:split 与 join——文本处理的左手右手
line = "小明,88,92,79"
# split:字符串 → 列表
parts = line.split(",")
print(parts) # ['小明', '88', '92', '79']
name, *scores = parts # 星号收走剩余部分
print(name, scores) # 小明 ['88', '92', '79']
print("a b c".split()) # 不传参:按任意空白切,自动去空串
# ['a', 'b', 'c']
# join:列表 → 字符串(注意是字符串调用它)
print("-".join(["2026", "10", "06"])) # 2026-10-06
print(", ".join(["Python", "Java", "Go"])) # Python, Java, Go
# 拼接大量字符串的正确姿势:join,而不是 += 循环累加
lines = ["第一行", "第二行", "第三行"]
print("\n".join(lines))
['小明', '88', '92', '79']
小明 ['88', '92', '79']
['a', 'b', 'c']
2026-10-06
Python, Java, Go
第一行
第二行
第三行
split 和 join 是一对镜像操作,CSV 行、路径、日志解析全靠它们来回转换。
步骤 5:查找判断一族
filename = "report_2026.pdf"
# startswith / endswith:判断前缀后缀(过滤文件常用)
print(filename.endswith(".pdf")) # True
print(filename.startswith("report")) # True
# find:找到返回下标,找不到返回 -1(index 找不到会抛异常)
print(filename.find("_")) # 6
print(filename.find("xyz")) # -1
# count:统计子串出现次数
print("banana".count("an")) # 2
# isdigit / isalpha:类型校验(转换 int 前先检查)
print("123".isdigit()) # True
print("12a".isdigit()) # False
True
True
6
-1
2
True
False
步骤 6:转义字符与原始字符串
# 常见转义
print("第一行\n第二行") # \n 换行
print("姓名:\"小明\"") # \" 输出双引号
print("C:\\Users\\test") # \\ 输出一个反斜杠
# Windows 路径的正确姿势:原始字符串 r""
path = r"C:\Users\test\file" # r 前缀:反斜杠不当转义符
print(path)
# 三引号:跨行字符串
html = """
<html>
<body>hello</body>
</html>
"""
print(html)
第一行
第二行
姓名:"小明"
C:\Users\test
C:\Users\test\file
<html>
<body>hello</body>
</html>
讲清楚要点
- 字符串不可变:
s[0] = "X" 直接 TypeError。所有「修改」类方法(strip/replace/upper…)都返回新字符串,必须重新绑定。字符串不可变换来的是可 hash(能当 dict 键)、多引用共享安全。
- f-string 是默认选择:大括号里可以放变量、表达式、函数调用;冒号后接格式说明(
.2f、>10、,、%)。大括号本身要输出就写 {{ 和 }}。
- split/join 是文本转换的轴心:
str → list 用 split,list → str 用 join。注意 join 是分隔符字符串的方法:",".join(lst)。
- 不传参的 split() 按任意空白切:多个空格、制表符、换行全算一个分隔符,还会丢弃首尾空白——清洗脏文本比
split(" ") 靠谱得多。
- find 返回 -1,index 抛异常:不在乎位置只判断「有没有」用
in;要下标用 find(温和)或 index(严格)。
- Windows 路径记得 r 前缀:
r"C:\new\test" 里的 \n 不会被当成换行。写路径更推荐第 8 课的 pathlib,但它内部也依赖这个常识。
- 判断空字符串用
if not s::空串、None 都为假;if s == "" 漏掉 None 的情况。
你可能会懵的地方
-
strip() 能去掉字符串中间的空格吗?
不能,strip 只处理两端。去中间空格用 s.replace(" ", "") 或 "".join(s.split())。想只去左边/右边用 lstrip/rstrip。
-
单引号、双引号、三引号怎么选?
功能几乎等价,惯例:普通字符串用单引号,字符串里含单引号时用双引号("it's"),跨行或含两种引号时用三引号。PEP 8 不强制,团队统一即可。
-
"3" + "5" 和 "3" * 3 是什么结果?
"35" 和 "333"。+ 是拼接、* 是重复——对字符串做算术符号是这两层含义,和数字运算完全不同。"3" - "5" 则直接 TypeError。
-
怎么遍历字符串?
和列表一样 for ch in s:,每个 ch 是单字符字符串。带下标用 for i, ch in enumerate(s):。字符串本质是字符序列,切片、in、len 全都通用。
-
中文字符串的 len 是字符数还是字节数?
Python 3 的 str 是 Unicode,len("你好") 是 2(字符数)。涉及文件大小、网络传输的「字节数」要把字符串编码后算:len("你好".encode("utf-8")) 是 6。
错法对照
| 错法 | 问题 | 正解 |
|---|
s.strip() 不接返回值 | 原串不变,等于白做 | s = s.strip() |
s[0] = "X" | 字符串不可变,TypeError | s = "X" + s[1:] 重建 |
循环里用 += 拼大字符串 | 每次都新建串,O(n²) | "".join(parts) 一次拼接 |
s.split(" ") 处理脏文本 | 连续空格产生空串元素 | s.split() 不传参 |
写 Windows 路径 "C:\new\test" | \n 被解释为换行 | r"C:\new\test" 原始字符串 |
用 == 判断字符串是否为空之外的逻辑 | 混淆空串与 None | 判空 if not s:,判 None if s is None: |
"age" + 18 | str + int 报 TypeError | f"age{18}" 或 str(18) |
卡点急救
- replace/strip 看似没生效:方法返回新串,原串不动。接住返回值:
s = s.strip().lower()。
- TypeError: can only concatenate str:把数字直接和字符串相加。改 f-string 或
str() 转换。
- split 出来的数字不能运算:split 返回的都是字符串,逐个
int(x) 后再算;可用推导式 [int(x) for x in parts]。
- join 报 TypeError: sequence item 0: expected str instance:列表里混了非字符串(如数字)。先转换:
",".join(str(x) for x in lst)。
- UnicodeDecodeError 读写文件时出现:编码不一致。文件读写显式传
encoding="utf-8"(第 8 课详述)。
面试常问
-
字符串是不可变的,这样设计有什么好处?
①可 hash,能作为 dict 键和 set 元素;②多引用共享同一对象安全,无需防御性拷贝;③实现上可做驻留(interning)与缓冲优化。代价是每次「修改」都产生新对象,大量拼接需用 join 优化。
-
为什么拼接大量字符串推荐 join 而不是 +=?
+= 每轮都可能创建新字符串并复制旧内容,整体 O(n²);join 先算总长一次性分配再填充,O(n)。数据量大时差距显著。
-
split() 和 split(" ") 的区别?
无参 split 按连续任意空白(空格/制表/换行)切分且忽略首尾空白,不产生空串;split(" ") 严格按单个空格切,连续空格会产生空字符串元素。清洗自然文本用无参版本。
-
Python 如何反转字符串?几种方法?
最惯用 s[::-1];也可 "".join(reversed(s))。顺带能答:字符串是序列,切片步长 -1 即逆序。
-
字符串前缀 b、r、f、u 分别什么意思?
b 字节串(bytes);r 原始字符串(反斜杠不转义,常用于正则与路径);f 格式化字符串(3.6+);u 在 Python 3 中无实际作用(3 的 str 本就是 Unicode,仅兼容 Python 2 写法)。
自测三问
"Hello, World" 取出 World,写出切片;反转整个字符串写出切片。
"{:>8.2f}".format(3.14159) 输出什么(或用 f-string 等价写法)?
"a=1,b=2,c=3" 解析成 {"a": "1", "b": "2", "c": "3"},写出代码。
小结一句话
字符串是不可变的字符序列:切片截取、f-string 格式化、split/join 互相转换、方法一律返回新串要接住——这四条撑起脚本世界的文本处理。
延伸