在Python文本处理中,从一段字符串里提取某个关键词之前或之后的内容,是非常常见的需求。例如从接口返回的错误信息中剥离出关键标识,或从配置行中分离键与值。不同场景下关键词可能出现一次、多次甚至不出现,选用的截取方式也应有所不同。

一、基础方案:使用find与切片
当确定关键词在字符串中只出现一次,且必定存在时,可以用str.find定位索引,再通过切片拿到前后子串。这种方式直观、性能好,适合受信任的数据源。
下面的示例演示如何提取“@”符号前后的邮箱用户名与域名部分。若关键词不存在,find返回-1,此时直接切片会得到整个字符串或空串,需要额外判断。
text = "user@ipipp.com"
key = "@"
idx = text.find(key)
if idx != -1:
before = text[:idx]
after = text[idx+len(key):]
print(before) # user
print(after) # ipipp.com
else:
print("关键词未找到")
该方法的优点是实现简单、无额外依赖;缺点是面对重复关键词时只能拿到第一次出现的位置。如果业务要求取最后一次出现,应改用rfind。
对于可能缺失关键词的场景,务必在截取前做索引检查,否则在后续逻辑里误用错误子串会导致数据异常。封装成函数时建议明确返回元组或抛出自定义异常。
二、处理重复关键词:rfind与rsplit
日志行常以相同分隔符出现多次,例如“时间|级别|模块|信息”。若只想取最后一个“|”之后的信息体,用rfind定位末次下标最直观。
另一种更优雅的写法是rsplit,它从右向左切割,限制最大分割次数,可一步拿到所需片段,代码可读性更高。
log = "2024-01-01|ERROR|auth|token expired"
# 使用rfind
idx = log.rfind("|")
msg1 = log[idx+1:] if idx != -1 else ""
# 使用rsplit
parts = log.rsplit("|", 1)
msg2 = parts[-1] if len(parts) > 1 else ""
print(msg1) # token expired
print(msg2) # token expired
rsplit的优势在于不需要手动判断索引,当分隔符不存在时返回仅含原串的列表,逻辑更紧凑。在提取路径中文件名、URL中查询参数等场景同样适用。
需要注意的是,如果关键词是多字符且可能重叠,rsplit按完整子串匹配,不会处理重叠情况,这通常与业务预期一致,但写通用函数时要心里有数。
三、复杂匹配:正则表达式提取
当关键词本身不固定,或需要按模式(如“订单号:XXXX”)提取时,正则是最灵活的手段。通过捕获组可以一次拿到关键词前、关键词、关键词后的内容。
以下代码从文本中提取“订单号:”后面的数字,并同时获取该词之前的句子片段。正则的re.search在首次匹配即返回,若需全部匹配可用re.finditer。
import re
text = "用户反馈订单号:88231已发货,请查收"
pattern = r"(.*?)(订单号:)(d+)"
m = re.search(pattern, text)
if m:
before = m.group(1) # 用户反馈
key = m.group(2) # 订单号:
after = m.group(3) # 88231
print(before, key, after)
</p>
<p>正则方案表达能力极强,但代价是性能略低与写法复杂度上升。建议在关键词模式多变、或需同时校验格式时使用;若仅是固定串截取,前两种方案更轻量。</p>
<p>使用正则时应对特殊字符转义,例如关键词含“.”或“*”时要写成<code>re.escape</code>,防止误当成元字符。封装工具函数时可将关键词参数自动转义,提升安全性。</p>
<h2>四、封装通用提取函数</h2>
<p>综合上述情况,可封装一个支持“取前、取后、取末次”的通用函数,让调用方通过参数控制行为,降低重复代码。</p>
<p>下面示例提供<code>side</code>参数指定截取方向,<code>last</code>参数控制是否取末次出现,并对缺失情况返回默认值,保证调用处不崩溃。</p>
<pre class=brush:python;toolbar:false>
def extract_around(text, key, side="after", last=False, default=""):
if last:
idx = text.rfind(key)
else:
idx = text.find(key)
if idx == -1:
return default
if side == "before":
return text[:idx]
elif side == "after":
return text[idx+len(key):]
else:
return text[:idx], text[idx+len(key):]
s = "name=Tom;name=Lucy"
print(extract_around(s, "name=", side="after", last=True)) # Lucy
该函数把边界判断集中处理,业务代码只需关注截取意图。在批量处理成千上万条记录时,统一入口也方便后续加入日志或监控。
如果团队中此类需求频繁,可进一步扩展支持正则模式与多关键词优先级,但核心思路仍是先定位、再切片、后兜底,保证稳健与可读。
五、常见误区与小结
初学者常直接用split(key)[0]或[1]来取前后子串,一旦关键词不存在就会因列表越界报错。另一种误区是忽略编码,处理含中文的字符串时未确认其为str类型,导致切片按字节错乱。
正确的做法是根据数据可信度与关键词特征选择方案:固定单次出现用find;末次出现用rfind或rsplit;模式多变用正则;并以封装函数收敛异常。这样既能写出清晰逻辑,也能让字符串截取成为可靠的基础工具。