跳至主要内容

Python 字符串查找与替换:in、.find()、.index()、.count() 和 .replace()

在现代 Python 中查找、计数与替换子字符串的实用范式。
· 8分钟 阅读

使用 AI 探索

我们常会想到用 .find() 或 .index() 来检查某段文本是否存在于字符串中——却又会遇到诸如抛出 ValueError 或漏掉字符串开头匹配等奇怪现象。大小写敏感、切片边界的 off-by-one,以及 str 上没有 .contains() 方法,这些都加剧了困惑。解决办法很直接:用对工具。存在性检查用 in 运算符,位置用 .find()/.index(),计数用 .count(),替换用 .replace()——当需忽略大小写时,再配合一些对 Unicode 友好的技巧。

Python 如何处理子字符串搜索

Python 的文本字符串是 Unicode。子字符串操作默认区分大小写,并由 C 实现以提升速度。判断某段文本是否出现在字符串中的最直接方式是使用 in 运算符,它通过字符串的 __contains__() 实现返回布尔值。需要位置或计数时,使用专用的 str 方法。可选的 start 和 end 参数遵循切片语义:start 包含;end 排除。

使用 in 判断包含关系

当您只关心子字符串是否存在时,使用 "needle" in haystack。它可读性强且避免了索引处理。

text = "Banana bread"
print("ban" in text)   # False (case-sensitive)
print("Ban" in text)   # True

若需不区分大小写的检查,请对双方进行归一化。与 .lower() 相比,.casefold() 对 Unicode 更敏感,是我进行无大小写匹配时的默认选择。

pattern = "ban"
print(pattern.casefold() in text.casefold())  # True
  • 适用场景:快速存在性检查、输入校验、列表过滤。
  • 边界情况:"" in s 始终为 True(空字符串存在于每个字符串中)。

str.find() 方法

语法

str.find() 返回子字符串的最小索引,若未找到则返回 -1。可选的 start 与 end 可限制搜索范围。

s.find(substring, start=0, end=len(s))

示例与用法

当您需要位置并且可以在未找到时处理返回值而非异常时,请使用 .find()。

tagline = "Ship fast. Fix faster."
print(tagline.find("fast"))         # 5
print(tagline.find("fast", 6))      # 16
print(tagline.find("slow"))         # -1

不要写 if s.find(sub): 来检查存在性。如果匹配位于索引 0,该条件为假。

name = "Alice"
if name.find("A"):         # Wrong: 0 is Falsey
    print("Found A")       # Will not run
if name.find("A") != -1:   # Correct
    print("Found A")

str.index() 方法

语法

.index() 与 .find() 类似,但当子字符串缺失时会抛出 ValueError。

s.index(substring, start=0, end=len(s))

示例与选择时机

当子字符串必须存在,且抛出异常是合适的失败方式时,使用 .index()。

product = "USB-C Cable"
print(product.index("Cable"))  # 6

try:
    product.index("HDMI")
except ValueError:
    print("Required label missing")

str.count() 方法

语法

.count() 返回不重叠出现的次数。它也接受 start 与 end 边界。

s.count(substring, start=0, end=len(s))

示例与边界情况

在无需正则的前提下,使用 .count() 快速统计。

log_line = "ERROR: timeout. ERROR: retry. ERROR: gave up."
print(log_line.count("ERROR"))     # 3
print(log_line.count("WARN"))      # 0

特殊情况:空字符串。

print("abc".count(""))   # 4 (len(s) + 1)

str.replace() 方法

.replace() 返回一个新字符串,其中所有 old 的出现都被 new 替换。使用 count 限制替换次数。

s.replace(old, new, count=-1)

替换子字符串

该方法不是就地操作;它会返回副本。

message = "The red house is between the blue house and the old house"
print(message.replace("house", "car"))
# The red car is between the blue car and the old car

print(message.replace("house", "car", 2))
# The red car is between the blue car and the old house

不区分大小写的替换

若需在替换时不区分大小写并保留原有大小写模式,可使用带 re.IGNORECASE 的 re.sub();如果不需要保留原大小写,可用 .casefold() 进行归一化。

使用 .casefold() 进行不区分大小写的匹配

.casefold() 提供更强的、对 Unicode 友好的大小写归一化,在无大小写匹配时优于 .lower()。

query = "straße"           # German 'straße'
text  = "STRASSE und mehr" # Uppercase variant
print(query.casefold() in text.casefold())  # True

用于前缀与后缀的相关工具

若已知开头/结尾,请使用专用方法——它们比切片或正则更快、更清晰:

  • s.startswith(prefix) 与 s.endswith(suffix)(接受元组以指定多个选项)
  • s.removeprefix(prefix) 与 s.removesuffix(suffix)(Python 3.9+)
filename = "draft_report_v2.pdf"
print(filename.removeprefix("draft_"))  # report_v2.pdf
print(filename.endswith((".pdf", ".docx")))  # True

处理 Pandas 列

当在 DataFrame 的一列中搜索大量字符串时,使用向量化字符串方法。将 regex=False 设置为匹配字面文本。

import pandas as pd

cities = pd.Series(["New York", "Yorktown", None, "Toronto"], dtype="string")
mask = cities.str.contains("York", regex=False, na=False)
print(mask.tolist())  # [True, True, False, False]

注意:

  • 默认情况下 regex=True,因此像 . 这样的特殊字符会匹配任意字符,除非您禁用正则。
  • 通过 na=... 处理空值。使用 StringDtype 时,缺失值在掩码中默认为 False。

常见陷阱与边界情况

  • str 上没有 .contains():请用 "sub" in s。"abc".contains("a") 会抛出 AttributeError。

  • .find() 的真值问题:索引 0 为假;请与 != -1 比较,或优先用 in 做存在性检查。

  • .index() 会抛异常:若子字符串可能不存在,请处理 ValueError。

  • 空子字符串:"" in s 为 True;s.find("") 返回 0;s.count("") 为 len(s)+1。

  • 大小写敏感:除非您进行归一化或使用正则标志,否则所有操作均区分大小写。

  • 字节与文本:不要在搜索中混用 bytes 与 str;请解码或编码以匹配类型。

  • 子字符串语义:"ab" in "cat,bat" 为 False,尽管 "a" 与 "b" 分别出现——匹配必须是连续的。

可运行的动手示例

这些简短代码片段展示了正确范式,并强调了常见陷阱。

存在性检查

headline = "Where's Waldo?"
print("Waldo" in headline)          # True
print("Wenda" in headline)          # False
print(headline.find("Waldo"))       # 8
print(headline.find("Wenda"))       # -1

带边界的搜索(start 与 end)

end 是排他的,因此搜索位置与切片语义一致。

headline = "Where's Waldo?"
print(headline.find("Waldo", 0, 6))   # -1 (searching "Where'")
print(headline.find("Waldo", 0, 13))  # 8  (searching up to the 'o' in Waldo)

计数与替换

inventory = "apple, apple, pear, apple"
print(inventory.count("apple"))                # 3
print(inventory.replace("apple", "orange", 2)) # orange, orange, pear, apple

Python 字符串查找与替换练习示例 

此示例处理一组电影短评。它演示了存在性检查、带边界的搜索,以及无需正则的安全替换。

blurbs = [
    "Critics say the lead actor delivers a career-best performance.",
    "Fans argue the actor actor scene was intentionally repetitive.",
    "A clever cameo steals the show."
]

for blurb in blurbs:
    # Check whether "actor" appears between indexes 20 and 40 (end exclusive).
    window_has_actor = "actor".casefold() in blurb[20:40].casefold()
    if not window_has_actor:
        print("Window check: actor not found")
        continue

    # If "actor actor" appears, collapse to a single "actor".
    if "actor actor" in blurb:
        print(blurb.replace("actor actor", "actor"))
    # If "actor actor actor" appears, collapse to a single "actor".
    elif "actor actor actor" in blurb:
        print(blurb.replace("actor actor actor", "actor"))
    else:
        print(blurb)

输出:

Window check: actor not found
Fans argue the actor scene was intentionally repetitive.
Window check: actor not found

结语

简单包含关系用 in 运算符;需要位置时用 .find() 或 .index();不重叠计数用 .count();替换用 .replace()。不区分大小写时用 .casefold() 归一化;在合适场景优先使用专用的前缀/后缀辅助方法。我们避免将 .find() 的结果直接转为布尔值,并将空子字符串的行为视为特殊情况,以确保字符串代码既正确又清晰。

主题