我正在尝试替换大海捞针的第N个外观。我想通过re.sub()简单地做到这一点,但似乎无法想出一个合适的正则表达式来解决这个问题。我想尝试改编:http://docstore.mik.ua/orelly/perl/cookbook/ch06_06.htm但是我想在跨越多线时失败了。
我当前的方法是一种迭代方法,它在每次突变后从头开始查找每个事件的位置。这是非常低效的,我想得到一些输入。谢谢!
答案 0 :(得分:3)
我认为你的意思是re.sub
。您可以传递一个函数并跟踪到目前为止调用它的频率:
def replaceNthWith(n, replacement):
def replace(match, c=[0]):
c[0] += 1
return replacement if c[0] == n else match.group(0)
return replace
用法:
re.sub(pattern, replaceNthWith(n, replacement), str)
但是这种方法感觉有点hacky,也许有更优雅的方式。
答案 1 :(得分:2)
像这样的正则表达式应该可以帮到你。虽然我不确定它的效率如何:
#N=3
re.sub(
r'^((?:.*?mytexttoreplace){2}.*?)mytexttoreplace',
'\1yourreplacementtext.',
'mystring',
flags=re.DOTALL
)
DOTALL标志很重要。
答案 2 :(得分:1)
你可以使用带有MatchObject.start()和MatchObject.end()的re.findall吗?
使用.findall查找字符串中所有模式的出现,使用.start / .end获取第N次出现的索引,使用索引创建具有替换值的新字符串?
答案 3 :(得分:1)
我一直在苦苦挣扎,但是我发现了一个我认为非常pythonic的解决方案:
>>> def nth_matcher(n, replacement):
... def alternate(n):
... i=0
... while True:
... i += 1
... yield i%n == 0
... gen = alternate(n)
... def match(m):
... replace = gen.next()
... if replace:
... return replacement
... else:
... return m.group(0)
... return match
...
...
>>> re.sub("([0-9])", nth_matcher(3, "X"), "1234567890")
'12X45X78X0'
编辑:匹配器由两部分组成:
alternate(n)
功能。这将返回generator,返回无限序列True / False,其中每第n个值为True。可以将其想象为list(alternate(3)) == [False, False, True, False, False, True, False, ...]
。
match(m)
功能。这是传递给re.sub
的函数:它获取alternate(n)
(gen.next()
)中的下一个值,如果它是True
,它将替换匹配的值;否则,它保持不变(用它自己替换)。
我希望这很清楚。如果我的解释是模糊的,请说出来,我会改进它。
答案 4 :(得分:0)
如果模式("针")或替换是一个复杂的正则表达式,你就不能假设任何东西。函数" nth_occurrence_sub"我提出的是一个更通用的解决方案:
def nth_match_end(pattern, string, n, flags):
for i, match_object in enumerate(re.finditer(pattern, string, flags)):
if i + 1 == n:
return match_object.end()
def nth_occurrence_sub(pattern, repl, string, n=0, flags=0):
max_n = len(re.findall(pattern, string, flags))
if abs(n) > max_n or n == 0:
return string
if n < 0:
n = max_n + n + 1
sub_n_times = re.sub(pattern, repl, string, n, flags)
if n == 1:
return sub_n_times
nm1_end = nth_match_end(pattern, string, n - 1, flags)
sub_nm1_times = re.sub(pattern, repl, string, n - 1, flags)
sub_nm1_change = sub_nm1_times[:-1 * len(string[nm1_end:])]
components = [
string[:nm1_end],
sub_n_times[len(sub_nm1_change):]
]
return ''.join(components)
答案 5 :(得分:0)
我有一个类似的功能,我写这样做。我试图复制SQL REGEXP_REPLACE()
功能。我最终得到了:
def sql_regexp_replace( txt, pattern, replacement='', position=1, occurrence=0, regexp_modifier='c'):
class ReplWrapper(object):
def __init__(self, replacement, occurrence):
self.count = 0
self.replacement = replacement
self.occurrence = occurrence
def repl(self, match):
self.count += 1
if self.occurrence == 0 or self.occurrence == self.count:
return match.expand(self.replacement)
else:
try:
return match.group(0)
except IndexError:
return match.group(0)
occurrence = 0 if occurrence < 0 else occurrence
flags = regexp_flags(regexp_modifier)
rx = re.compile(pattern, flags)
replw = ReplWrapper(replacement, occurrence)
return txt[0:position-1] + rx.sub(replw.repl, txt[position-1:])
我还没有看到提到的一个重要注意事项是,您需要返回match.expand()
,否则它无法正确扩展\1
模板并将其视为文字。
如果你希望这个工作,你需要以不同的方式处理标志(或从my github获取它,它很容易实现,你可以通过设置它来为它进行虚拟处理到0
并忽略我对regexp_flags()
的号召。