Python 替换,使用数组中的模式

3 投票
4 回答
5721 浏览
提问于 2025-04-18 02:39

我需要用一个数组里的东西来替换字符串中的一些内容,它们可能长这样:

array = [3, "$x" , "$y", "$hi_buddy"]
#the first number is number of things in array
string = "$xena is here $x and $y."

我还有另一个数组,里面是用来替换这些内容的东西,假设叫它 rep_array。

rep_array = [3, "A", "B", "C"]

我用这个来进行替换:

for x in range (1, array[0] + 1):
  string = string.replace(array[x], rep_array[x])

但是结果是:

string = "Aena is here A and B."

不过我只想匹配单独的 $x,而不是包含在其他单词里的 $x。结果应该是这样:

string = "$xena is here A and B."

注意:

  • 所有在 array 中的模式都以 $ 开头。
  • 一个模式只有在完全匹配 $ 后面的整个单词时才算匹配;比如 $xena 不匹配 $x,但 foo$x 匹配。
  • $ 可以用 @ 来转义,这样就不会被匹配(例如 $x 不匹配 @$x)。

4 个回答

0

你也可以试试这样的写法:

import re

search = ["$x" , "$y", "$hi_buddy"]
replace = ["A", "B", "C"]
string = "$xena is here $x and $y skip$x."

repl = dict(zip(search, replace))
print re.sub(r'\B\$\w+', lambda m: repl.get(m.group(0), m.group(0)), string)

# result: $xena is here A and B skip$x.

\B 在这里的意思是“当前面的字符不是字母或数字时,匹配 $”。如果你想让 skip$x 也被替换掉,只需要去掉 \B 就可以了:

print re.sub(r'\$\w+', lambda m: repl.get(m.group(0), m.group(0)), string)
# $xena is here A and B skipA.
0

string.replace 这个方法并不知道什么是正则表达式,所以你需要使用 re 模块(可以查看这个链接了解更多:https://docs.python.org/3.4/library/re.html)。具体来说,你要用 re.sub 这个方法:

>>>re.sub(r"\$x\b", "replace", r"$xenia $x")
'$xenia replace'
5

这不是直接回答你问题的内容,但我想你会看到其他人用 \b 的方法来解决问题,所以我想给你推荐一个更符合 Python 风格的解决方案:

rep_dict = {'x': 'A', 'y': 'B', 'hi_buddy': 'C'}
string = '{xena} is here {x} and {y}'

print string.format(rep_dict)

不过在这里,如果 rep_dict 中缺少 xena,会引发一个 KeyError 错误。这个问题可以通过那篇文章中的答案来解决,使用 defaultdict 或者根据你的需求选择一个格式化的方法。

使用 $ 的问题在于,想要匹配某个东西而不定义真实边界并不简单。大多数使用 $ 的语言会将它应用到下一个字符上,而在更大的字符上使用边界(这些是 shell 和 makefile),比如 ${xena}。像 Perl 这样的语言使用语法来定义 $ 变量的上下文,我猜它们在分词器中也可能使用正则表达式。

这就是为什么在 Python 中,我们只使用格式化操作符来标记字符串中变量的边界 {},而不使用多余的 $,这样就避免了歧义(比如 $xena => ${x}ena 还是 ${xena}?)。

希望这对你有帮助。

3

使用一个正则表达式,把你的源文本包裹起来,同时加上一些空白的查找和一个 \b 锚点;确保也包含字符串的开头:

import re

for pattern, replacement in zip(array[1:], rep_array[1:]):
    pattern = r'{}\b'.format(re.escape(pattern))
    string = re.sub(pattern, replacement, string)

这里用到 re.escape() 是为了确保模式中的任何正则表达式特殊字符都能被正确处理。zip() 用来把你的模式和替换值配对;这是比 range() 循环更符合 Python 风格的做法。

\b 只在一个单词字符后面跟着一个非单词字符(或者反过来)的位置匹配,也就是一个 单词边界。你的模式都以单词字符结尾,所以这确保了只有在 下一个 字符不是单词字符时,模式才会匹配,这样就阻止了 $x$xena 中匹配。

演示:

>>> import re
>>> array = [3, "$x" , "$y", "$hi_buddy"]
>>> rep_array = [3, "A", "B", "C"]
>>> string = "$xena is here $x and $y. foo$x matches too!"
>>> for pattern, replacement in zip(array[1:], rep_array[1:]):
...     pattern = r'{}\b'.format(re.escape(pattern))
...     string = re.sub(pattern, replacement, string)
... 
>>> print string
$xena is here A and B. fooA matches too!

撰写回答