前言
这一篇是把str的方法按用途梳理一遍。与其零散地记"有个strip、有个split",不如按你手上要解决的问题来归类:我要找东西、我要判断、我要切分、我要清理、我要改大小写、我要格式化。分类记住了,用到时自然能想起该翻哪一页。
要先纠正一个说法上的习惯:str的这些"函数"准确说是方法(method),得由字符串实例来调用,比如"abc".upper(),而不是upper("abc")。它们全都是读方法——str不可变,任何方法都不会改动调用它的那个字符串,只会返回一个新串。
另一个高频误解集中在strip:很多人以为strip("abc")是"去掉开头的 abc",其实它的参数是一组字符,会把两端所有落在这组字符里的字符逐个剥掉。
本文按功能分组列方法,签名以 Python 官方文档为准,代码基于 Python 3.8 及以上。Python 2.7 已于 2020 年 1 月 1 日停止维护,dict.iteritems()、xrange、unicode那些写法本文不采用。
一、查找与判断
查找类方法告诉你"在哪里"或"有没有"。
# 适用于 Python 3.8+
s = "Hello, World"
print(s.find("o")) # 4 找不到返回 -1
print(s.rfind("o")) # 8 从右往左找第一个
print(s.count("l")) # 3 非重叠出现次数
print(s.startswith("He")) # True
print(s.endswith("ld")) # True
print(s.startswith(("Hi", "He"))) # True 可传元组一次判多个s.find(sub)找不到返回-1,而s.index(sub)找不到会抛ValueError。要"自己判断在不在"就用find,要"找不到就是 bug"就用index。
判断类方法全都以is开头,返回布尔值:
| 方法 | 为真的条件 |
|---|
s.isalpha() | 非空且全是字母 |
s.isdigit() | 非空且全是数字字符 |
s.isalnum() | 非空且全是字母或数字 |
s.isspace() | 非空且全是空白 |
s.islower()/s.isupper() | 有大小写字符且全为小写 / 大写 |
s.isidentifier() | 是合法的 Python 标识符 |
# 适用于 Python 3.8+
print("abc".isalpha()) # True
print("abc1".isalpha()) # False
print("".isalpha()) # False 空串对 is* 一律返回 False
print("Hello".islower()) # False 有大写字母注意is系列对空串统一返回False,别把"空"当成"通过校验"。
二、切分与合并
| 方法 | 返回 | 关键点 |
|---|
s.split(sep=None, maxsplit=-1) | 列表 | sep=None时连续空白算一个分隔符 |
s.rsplit(sep=None, maxsplit=-1) | 列表 | 从右往左切 |
s.splitlines(keepends=False) | 列表 | 按各种换行符切行 |
s.partition(sep) | 三元组 | 保留分隔符,只切一刀 |
s.join(iterable) | 字符串 | 分隔符在前 |
# 适用于 Python 3.8+
print("a b c".split()) # ['a', 'b', 'c'] 连续空白算一个
print("a,b,,c".split(",")) # ['a', 'b', '', 'c'] 会留空串
print("a,b,c".rsplit(",", 1)) # ['a,b', 'c'] 从右只切一次
print("-".join(["a", "b"])) # a-bsplit()不传参数和split(",")是两套算法:前者把连续空白视作单个分隔符且不产生空串,后者严格按分隔符切、连续分隔符会切出空串。这一点是新手最常踩的坑之一。
join的参数必须全是字符串,混进数字会抛TypeError,得先map(str, ...)转换。
三、清理与填充
# 适用于 Python 3.8+
print(" x ".strip()) # x
print(" x ".lstrip()) # x 只去左边
print("xxabcxx".strip("x")) # abc 两端所有 x 都被剥掉
print("42".zfill(5)) # 00042
print("ab".center(6, "*")) # **ab**
print("ab".ljust(4, ".")) # ab..
print("ab".rjust(4, ".")) # ..abstrip、lstrip、rstrip的参数都是字符集合,不是完整前缀/后缀。要按前后缀去除,应改用 3.9 才加入的removeprefix/removesuffix:
# 适用于 Python 3.9+
print("TestHook".removeprefix("Test")) # Hook
print("MiscTests".removesuffix("Tests")) # Misc
print("BaseTestCase".removeprefix("Test")) # BaseTestCase 前缀不匹配就原样返回3.9 以前的替代写法是s[len(p):] if s.startswith(p) else s,虽然啰嗦但行为一致。
四、大小写与映射
# 适用于 Python 3.8+
print("Hello".upper()) # HELLO
print("Hello".lower()) # hello
print("hello world".title()) # Hello World
print("aBc".swapcase()) # AbC
print("Straße".casefold()) # strasse 比 lower 更彻底,适合做比较casefold()是lower()的强化版,专门用于忽略大小写的比较:德语ß在casefold()下会变成ss,而lower()不会。做"两个字符串是否等价"的判断时优先用casefold()。
str.translate配合str.maketrans能做批量单字符替换或删除:
# 适用于 Python 3.8+
print("hello".translate(str.maketrans("", "", "aeiou"))) # hll
print("abc".translate(str.maketrans("abc", "xyz"))) # xyz五、格式化
三种主流方式:
| 方式 | 写法 | 说明 |
|---|
| f-string | f"{name}" | 3.6 起;3.8 起支持=调试符;3.12 起可嵌套同类引号 |
str.format | "{0}".format(x) | 位置 / 关键字都能用,还有格式规格 |
% | "%s" % x | 老式写法,新代码不建议 |
# 适用于 Python 3.8+
name, score = "小明", 95.6
print(f"{name} 的分数是 {score:.1f}") # 小明 的分数是 95.6
print("{0} 的分数是 {1:.1f}".format(name, score))
print(f"{score=}") # score=95.6 调试符,3.8 起:.1f这类"冒号后面"的部分叫格式规格(format spec),f"{x:.1f}"和"{x:.1f}".format(x=...)共用同一套语法,两边的规格可以互通。3.12 之前,f-string 里不能再嵌套同一种引号;3.12 起(PEP 701)放开了这个限制。
实战:清洗一段半结构化的文本
下面这段把查找、切分、清理、判断串起来,把一段乱糟糟的文本整理成结构化记录。
# 适用于 Python 3.8+
RAW = """
alice , 28 , Engineer
bob,31,Designer
carol , 45 , Manager
"""
def parse_records(raw):
records = []
for line in raw.splitlines():
line = line.strip()
if not line: # 跳过空行
continue
parts = [p.strip() for p in line.split(",")]
if len(parts) != 3: # 字段数不对,跳过
continue
name, age, job = parts
if not age.isdigit(): # 年龄必须是纯数字
continue
records.append({"name": name, "age": int(age), "job": job})
return records
for r in parse_records(RAW):
print(f"{r['name']:<6}|{r['age']:>3}|{r['job']}")
# alice | 28|Engineer
# bob | 31|Designer
# carol | 45|Managersplitlines()负责切行,strip()去掉行首尾空白,列表推导式里的strip()去掉每个字段两侧的空白,isdigit()做字段校验。每一步都围绕"用对方法"而不是写一堆手写循环。
常见坑点
- 把
strip当"去前后缀"
❌ 以为参数是前缀字面量:"www.example.com".strip("cmowz.")期待只动开头
✅ 参数是字符集合,两端凡属于该集合的字符都被逐个剥掉,官方文档给的这个例子结果就是example;按前后缀去除要用"www.example.com".removeprefix("www.")(3.9 起)
split()和split(",")混用
❌ 处理逗号分隔的 CSV 时写line.split(),字段里的空格被当成分隔符
✅ 明确分隔符时写line.split(",");只有"按空白分词"才用无参split()
find返回 -1 直接拿去当索引
❌s[s.find("x") + 1:],没找到时返回整串
✅ 先判-1,或改用partition并检查sep是否为空
is*方法对空串返回 False
❌if name.isalpha():于是空名字被当成"不合法",逻辑方向写反
✅ 想表达"仅当非空且全字母"用name.isalpha();想表达"要么是字母要么是空",写成name == "" or name.isalpha()
join里混入非字符串
❌",".join([1, 2, 3])→TypeError
✅",".join(map(str, [1, 2, 3]))
- 方法返回值没接住
❌s.strip()单独一行,以为s变了
✅s = s.strip(),所有str方法都返回新串
- 用
lower()做大小写无关比较
❌"Straße".lower() == "STRASSE".lower()为False
✅ 用casefold():"Straße".casefold() == "strasse".casefold()为True
- 沿用 Python 2 的旧写法
❌string.upper(s)的函数式调用、unicode(s)、s.decode("utf-8")处理普通str
✅ Python 3 用方法式s.upper(),类型统一是str;Python 2.7 已于 2020 年 1 月 1 日 EOL
总结
| 用途 | 代表方法 |
|---|
| 查找 | find/rfind/count/startswith/endswith |
| 判断 | isalpha/isdigit/isalnum/isspace |
| 切分 | split/rsplit/splitlines/partition |
| 合并 | join |
| 清理 | strip/lstrip/rstrip/removeprefix/removesuffix |
| 大小写 | upper/lower/casefold/title/swapcase |
| 映射 | translate+maketrans |
| 格式化 | f-string /str.format |
str的方法看着多,但分类很干净,记住"查找、判断、切分、合并、清理、大小写、格式化"这七类,用到时按需求对号入座就行。真正需要反复提醒的只有两点:这些都是方法不是函数,以及它们一律返回新串——str不可变,没有任何一个方法会就地改动原来的字符串。