将字符串 'love' 更改为 'live'。
写这个小需求的时候我正啃着昨晚的半拉烧饼…产品突然在群里丢一句:把所有 love 改成 live,别动别的。好嘞好嘞,别急,我边吃边改,顺手把几种写法和坑都记下来,省得以后大家再踩。
最省事的:str.replace
不讲武德的那种,全局替换,爱谁谁,简单粗暴。
text = "love is love, my glove is lovely."
print(text.replace("love", "live"))
# 输出: live is live, my glive is lively.
你看问题了吧?glove 也被打穿了,这一般不行。产品说的“把 love 改成 live”,十有八九是按“单词”算的。
只替换“完整单词”的:正则加单词边界
这个就靠谱多了,\b 是单词边界(字母与非字母之间)。
import retext = "love is love, my glove is lovely."
print(re.sub(r"\blove\b", "live", text))
# 输出: live is live, my glove is lovely.
如果你文本里还有大小写混合的 “Love/LOVE”…那就带上 IGNORECASE,再顺手把大小写样式也保住。
大小写样式也要跟着变(Love→Live、LOVE→LIVE)
回调函数走一把,按匹配结果的样式去构造替换词:
import redefto_live(m):
w = m.group()
if w.isupper():
return"LIVE"
if w[0].isupper():
return"Live"
return"live"
text = "love Love LOVE LoVe"
print(re.sub(r"\blove\b", to_live, text, flags=re.IGNORECASE))
# 输出: live Live LIVE Live
只改前几个,别全改:控制替换次数
就像“只改第一处”,产品临时变卦也常见:
text = "love love love"
print(re.sub(r"\blove\b", "live", text, count=1))
# 输出: live love love
中英文混排、标点古怪?多想一步
\b 在“love,”这种场景也好用,但如果你文本里把 _ 当字母用(程序标识符),那就别用 \b,换成负向断言更稳:
import re
text = "love,love_love (love) love2"
print(re.sub(r"(?<!\w)love(?!\w)", "live", text))
# 输出: live,love_love (live) love2
批量改整批文件:保存备份,别手抖
晚上十一点半在公司楼下吹风的时候…哦不对,回到正题。一次性改一堆文本文件,记得先备份。
from pathlib import Path
import re
import shutilpattern = re.compile(r"\blove\b", flags=re.IGNORECASE)
defto_live(m):
w = m.group()
if w.isupper(): return"LIVE"
if w[0].isupper(): return"Live"
return"live"
root = Path("docs")
for p in root.rglob("*.txt"):
bak = p.with_suffix(p.suffix + ".bak")
shutil.copy2(p, bak)
text = p.read_text(encoding="utf-8", errors="ignore")
new_text = pattern.sub(to_live, text)
if new_text != text:
p.write_text(new_text, encoding="utf-8")
二进制/日志流里“硬改字节”
有时候你拿到的是 bytes,就按字节替换,简单有效,但区分大小写,也不懂边界:
data = b"... love\x20is\x20love ..."
print(data.replace(b"love", b"live"))
别把别的词误伤:只在特定列或字段里改
比如 CSV 只改第 3 列(内容列),标题行、链接别碰:
import csv, repattern = re.compile(r"\blove\b", re.IGNORECASE)
defto_live(m):
w = m.group()
if w.isupper(): return"LIVE"
if w[0].isupper(): return"Live"
return"live"
with open("posts.csv", newline="", encoding="utf-8") as f, \
open("posts.out.csv", "w", newline="", encoding="utf-8") as w:
r = csv.reader(f)
out = csv.writer(w)
header = next(r, None)
if header: out.writerow(header)
for row in r:
row[2] = pattern.sub(to_live, row[2]) # 只改第3列
out.writerow(row)
Unicode 的小心思:规范化再匹配
极少见,但真实出现过:看起来是同一个字母,其实是全角/组合字符。unicodedata.normalize 一下再匹配更稳。
import unicodedata, repattern = re.compile(r"\blove\b", re.IGNORECASE)
deffix_text(s: str) -> str:
s = unicodedata.normalize("NFKC", s)
return pattern.sub("live", s)
print(fix_text("love is love"))
# 输出: live is live
单测两口子,防回归
别笑,我真被“glove 被改坏”这种回归坑过。
import rePAT = re.compile(r"\blove\b", re.IGNORECASE)
defsub_live(s):
return PAT.sub("live", s)
cases = {
"love is love": "live is live",
"glove": "glove",
"Love! LOVE?": "Live! LIVE?",
}
for k, v in cases.items():
assert sub_live(k) == v
性能两句闲话
量很大(几 GB 文本)的时候,流式读写一行一行改,比一次性读内存安全。 模式固定,就把 re.compile放外面复用。千万别想着把 o全局换成i:str.maketrans/translate是快,但会把move/lot也干掉,除非需求就是“把所有 o 变成 i”,那是另一个任务了。
行了差不多就这些,反正核心就两步:边界匹配 + 大小写样式,该快的快,该稳的稳。先这样,我去续杯水,等会儿有人要我把 “live” 又改回 “love” 我真不干…
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB,点击下方公众号回复关键字 python 全部免费领