Python技术迷

将字符串 '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 re

text = "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 re

defto_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 shutil

pattern = 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, re

pattern = 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, re

pattern = 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 re

PAT = 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 全部免费领