Python 替换,使用数组中的模式
我需要用一个数组里的东西来替换字符串中的一些内容,它们可能长这样:
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 个回答
你也可以试试这样的写法:
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.
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'
这不是直接回答你问题的内容,但我想你会看到其他人用 \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}?
)。
希望这对你有帮助。
使用一个正则表达式,把你的源文本包裹起来,同时加上一些空白的查找和一个 \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!