【问题标题】:Python replace, using patterns in arrayPython替换,使用数组中的模式
【发布时间】:2014-05-28 04:43:46
【问题描述】:

我需要用数组替换字符串中的一些东西,它们看起来像这样:

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

【问题讨论】:

  • 所有替换模式都以$开头吗?
  • 为什么不使用 python 中的string formats,而不是重新发明轮子?
  • @MartijnPieters: ` \$x ` 怎么样,在替换数组中你会有 ` A `?
  • @npinti:对不起,我不明白你的意思。
  • @Alfe:这就是为什么我问这个模式是否总是以$开头。

标签: python arrays regex replace


【解决方案1】:

这不是您问题的直接答案,但我猜您会在 \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,这可以通过 answers to that question 解决,使用 defaultdict 或您可能喜欢的格式化程序,具体取决于您的用例。

使用$ 的问题在于,制作不定义真实边界的匹配项并非易事。大多数使用$ 变量的语言将其应用于下一个字符,使用较大字符(即shell 和makefile)的边界,即${xena}。像 Perl 这样的语言使用语法来定义 $ 变量的上下文,我猜他们也可能在分词器中使用正则表达式。

这就是为什么在python中,我们只使用格式化运算符来标记字符串中变量{}的边界,没有无用的$所以我们不必处理歧义($xena => ${x}ena or ${xena}?)。

HTH

【讨论】:

  • 当然,我给出这个是为了让 OP 知道并考虑使用它,如果它可以成为他的一个选项,并且对于可能考虑在字符串中使用 $ 变量的未来读者已为其构建字符串格式的用例。 ;-)
  • 这是正确的 TOOWTDI (wiki.python.org/moin/TOOWTDI),如果 OP 对输入字符串有任何权力。
【解决方案2】:

使用正则表达式,用一些空白后视和\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() 循环的更 Pythonic 替代方案。

\b 仅匹配单词字符后跟非单词字符(反之亦然)的位置,即单词边界。您的模式都以单词字符结尾,因此这可以确保您的模式仅在 next 字符不是单词字符时匹配,从而阻止 $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!

【讨论】:

  • 你的解决方案几乎是我需要的方式,我还需要能够用@转义$,你能告诉我这种模式有什么问题吗?模式 = r'(?:([^@])|^){}\b'.format(re.escape(pattern))
  • @Whitedracke:你需要一个回顾:r'(?:(?<=[^@])|^){}\b'
  • @Whitedracke:或者更好的是,消极的后视:r'(?<!@){}\b'
【解决方案3】:

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'

【讨论】:

  • 这也将匹配 $x 中的 foo$x
  • 我需要替换 foo$x,但不知道转义 '\' 是如何进入数组的。
  • @Whitedracke:这是一个重要的细节; 在你的问题帖子中包含它!
  • @Whitedracke:我更新了您的帖子以包含该详细信息,以及所有模式都以 $ 开头的事实。正是这样的细节在什么是正确的解决方案和什么不是正确的解决方案方面产生了巨大的差异。
【解决方案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.

【讨论】:

  • 使用\B表示$$x也匹配。
  • @MartijnPieters:对,!$x...$x 和类似的也是如此。我从这个问题中不明白这是否是一种期望的行为。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-11-10
  • 2012-03-05
  • 1970-01-01
  • 2016-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多