替换字符串中第 n 次出现的子字符串
Posted
技术标签:
【中文标题】替换字符串中第 n 次出现的子字符串【英文标题】:Replace nth occurrence of substring in string 【发布时间】:2016-05-07 14:29:09 【问题描述】:我想替换字符串中第 n 个出现的子字符串。
必须有一些与我想做的事情相当的事情
mystring.replace("substring", 2nd)
实现这一目标的最简单和最 Pythonic 的方法是什么?
为什么不重复:我不想在这种方法中使用正则表达式,我发现的类似问题的大多数答案只是正则表达式剥离或非常复杂的函数。我真的想要尽可能简单而不是正则表达式的解决方案。
【问题讨论】:
回复:不重复:请阅读第一个答案。 @TigerhawkT3 啊,我明白了。他要求只有正则表达式的解决方案,但他也得到了非正则表达式的答案。我没有读过它们。你能编辑他的问题吗? OP 想要一个非正则表达式的解决方案,骗子有 99% 的基于正则表达式的解决方案 【参考方案1】:您可以使用带有str.find
的while 循环来查找第n 个匹配项(如果存在)并使用该位置创建新字符串:
def nth_repl(s, sub, repl, n):
find = s.find(sub)
# If find is not -1 we have found at least one match for the substring
i = find != -1
# loop util we find the nth or we find no match
while find != -1 and i != n:
# find + 1 means we start searching from after the last match
find = s.find(sub, find + 1)
i += 1
# If i is equal to n we found nth match so replace
if i == n:
return s[:find] + repl + s[find+len(sub):]
return s
例子:
In [14]: s = "foobarfoofoobarbar"
In [15]: nth_repl(s, "bar","replaced",3)
Out[15]: 'foobarfoofoobarreplaced'
In [16]: nth_repl(s, "foo","replaced",3)
Out[16]: 'foobarfooreplacedbarbar'
In [17]: nth_repl(s, "foo","replaced",5)
Out[17]: 'foobarfoofoobarbar'
【讨论】:
当出现 n 仅比允许的值大 1 (n = maximum_occurances + 1) 时,@wjandre 答案中有一个简单的错误。例如当:nth_repl('Blue headed racket tail', " ", "-", 4) 会产生'Blue headed racket tai-Blue headed racket tail' 请看我的回答:***.com/a/68456906/1970830 @Haider 这不是我的答案,是Padraic Cunningham 的。我刚刚编辑了它。【参考方案2】:我使用了简单的函数,它列出了所有出现的事件,选择第 n 个位置并使用它将原始字符串拆分为两个子字符串。然后它替换第二个子字符串中的第一次出现并将子字符串连接回新字符串:
import re
def replacenth(string, sub, wanted, n):
where = [m.start() for m in re.finditer(sub, string)][n-1]
before = string[:where]
after = string[where:]
after = after.replace(sub, wanted, 1)
newString = before + after
print(newString)
对于这些变量:
string = 'ababababababababab'
sub = 'ab'
wanted = 'CD'
n = 5
输出:
ababababCDabababab
注意事项:
where
变量实际上是匹配位置的列表,您可以在其中选择第 n 个。但是列表项索引通常以0
开头,而不是1
。因此有一个n-1
索引和n
变量是实际的第n 个子字符串。我的示例找到了第 5 个字符串。如果您使用n
索引并想找到第 5 个位置,则需要将n
设为4
。您使用的通常取决于生成我们的n
的函数。
这应该是最简单的方式,但也许不是最Pythonic的方式,因为
where
变量构造需要导入re
库。也许有人会找到更 Pythonic 的方式。
来源和一些附加链接:
where
建设:How to find all occurrences of a substring?
字符串拆分:https://www.daniweb.com/programming/software-development/threads/452362/replace-nth-occurrence-of-any-sub-string-in-a-string
类似问题:Find the nth occurrence of substring in a string
【讨论】:
如果指定的索引错误(例如太大),它会抛出错误,而不是什么都不做,这很烦人。 这有时有效,有时会失败交互式测试,它工作正常。但在我的代码中......拆分将sub
的第一个字符保留在before
中。如果我将其编辑为[n-1] - 1
,则它会正确拆分。什么给了?【参考方案3】:
我提出了以下建议,它还考虑了将所有“旧”字符串替换到左侧或右侧的选项。当然,没有替换所有匹配项的选项,因为标准 str.replace 可以完美运行。
def nth_replace(string, old, new, n=1, option='only nth'):
"""
This function replaces occurrences of string 'old' with string 'new'.
There are three types of replacement of string 'old':
1) 'only nth' replaces only nth occurrence (default).
2) 'all left' replaces nth occurrence and all occurrences to the left.
3) 'all right' replaces nth occurrence and all occurrences to the right.
"""
if option == 'only nth':
left_join = old
right_join = old
elif option == 'all left':
left_join = new
right_join = old
elif option == 'all right':
left_join = old
right_join = new
else:
print("Invalid option. Please choose from: 'only nth' (default), 'all left' or 'all right'")
return None
groups = string.split(old)
nth_split = [left_join.join(groups[:n]), right_join.join(groups[n:])]
return new.join(nth_split)
【讨论】:
【参考方案4】:最后一个答案几乎是完美的——只有一个更正:
def replacenth(string, sub, wanted, n):
where = [m.start() for m in re.finditer(sub, string)][n - 1]
before = string[:where]
after = string[where:]
after = after.replace(sub, wanted)
newString = before + after
return newString
替换后的字符串必须再次存储在此变量中。 感谢您提供出色的解决方案!
【讨论】:
和@J.Warren - 当string
中的wanted
字符串少于要替换的sub
出现的序数时,您还应该保护代码。 【参考方案5】:
我已经调整了@aleskva 的答案,以便更好地使用正则表达式和通配符:
import re
def replacenth(string, sub, wanted, n):
pattern = re.compile(sub)
where = [m for m in pattern.finditer(string)][n-1]
before = string[:where.start()]
after = string[where.end():]
newString = before + wanted + after
return newString
replacenth('abdsahd124njhdasjk124ndjaksnd124ndjkas', '1.*?n', '15', 1)
这给出了abdsahd15jhdasjk124ndjaksnd124ndjkas
。注意使用?
使查询不贪婪。
我意识到问题明确指出他们不想使用正则表达式,但是能够以清晰的方式使用通配符可能很有用(因此我的回答)。
【讨论】:
【参考方案6】:def replace_nth_occurance(some_str, original, replacement, n):
""" Replace nth occurance of a string with another string
"""
all_replaced = some_str.replace(original, replacement, n) # Replace all originals up to (including) nth occurance and assign it to the variable.
for i in range(n):
first_originals_back = all_replaced.replace(replacement, original, i) # Restore originals up to nth occurance (not including nth)
return first_originals_back
【讨论】:
【参考方案7】:聚会有点晚了,但我会认为这种方式非常pythonian(据我理解的意思),它不需要for循环或计数器
def Nreplacer(string,srch,rplc,n):
Sstring = string.split(srch)
#first check if substring is even present n times
#then paste the part before the nth substring to the part after the nth substring
#, with the replacement inbetween
if len(Sstring) > (n):
return f'srch.join(Sstring[:(n)])rplcsrch.join(Sstring[n:])'
else:
return string
【讨论】:
【参考方案8】:不是pythonic且效率不高,但单行是:
def replace_nth(base_str, find_str, replace_str, n):
return base_str.replace(find_str, "xxxxx", n-1).replace(find_str, replace_str, 1).replace("xxxxx", find_str)
如果您知道字符串中不存在某些“xxxxxx”占位符,则可以将第 n-1 次出现的位置替换为占位符。然后替换您要查找的子字符串的第 n 次出现,此时它是第一次出现。然后将所有占位符替换回原来的子串。
【讨论】:
【参考方案9】:我有类似的需求,即在日志中查找 IP 并选择性地仅替换 src IP 或 dst IP 字段。这就是我以pythonic方式实现的方式;
import re
mystr = '203.23.48.0 DENIED 302 449 800 1.1 302 http d.flashresultats.fr 10.111.103.202 GET GET - 188.92.40.78 '
src = '1.1.1.1'
replace_nth = lambda mystr, pattern, sub, n: re.sub(re.findall(pattern, mystr)[n - 1], sub, mystr)
result = replace_nth(mystr, '\S*\d+\.\d+\.\d+\.\d+\S*', src, 2)
print(result)
【讨论】:
【参考方案10】:一般解决方案:将子字符串 [pattern] 的任何指定实例替换为另一个字符串。
def replace(instring,pattern,replacement,n=[1]):
"""Replace specified instance(s) of pattern in string.
Positional arguments
instring - input string
pattern - regular expression pattern to search for
replacement - replacement
Keyword arguments
n - list of instances requested to be replaced [default [1]]
"""
import re
outstring=''
i=0
for j,m in enumerate(re.finditer(pattern,instring)):
if j+1 in n: outstring+=instring[i:m.start()]+replacement
else: outstring+=instring[i:m.end()]
i=m.end()
outstring+=instring[i:]
return outstring
【讨论】:
【参考方案11】:我的两分钱
a='01ab12ab23ab34ab45ab56ab67ab78ab89ab90';print('The original string: ', a)
sTar = 'ab';print('Look for: ', sTar)
n = 4; print('At occurence #:', n)
sSub = '***';print('Substitute with: ', sSub)
t = 0
for i in range(n):
t = a.find(sTar,t)
print(i+1, 'x occurence at', t)
if t != -1: t+=1
t-=1 #reset, get the correct location
yy = a[:t] + a[t:].replace(sTar, sSub, 1)
print('New string is:', yy)
输出
The original string: 01ab12ab23ab34ab45ab56ab67ab78ab89ab90
Look for: ab
At occurence #: 4
Substitute with: ***
1 x occurence at 2
2 x occurence at 6
3 x occurence at 10
4 x occurence at 14
New string is: 01ab12ab23ab34***45ab56ab67ab78ab89ab90
【讨论】:
【参考方案12】:优雅而简短:
def replace_ocurrance(string,from,to,num)
strange_char = “$&$@$$&”
return string.replace(from,strange_char,num).replace(strange_char, from,num-1).replace(to, strange_char,1)
【讨论】:
如果奇怪的字符对你来说不够陌生,你会生成一个 uuid 哈希......【参考方案13】:只有几个非正则表达式的答案,我想提供我自己的解决方案,我认为它更简单、更容易理解。创建一个新字符串并计算要替换的字符的第 n 次出现。
def replace_nth_occurence(old_str, old_char, new_char, n):
new_str = ""
occurences = 0
for s in old_str:
if s == old_char:
occurences += 1
if occurences == n:
new_str += new_char # append the new character instead of the old
else:
new_str += s
else:
new_str += s
return new_str
replace_nth_occurence("Testing_One_Two_Three", "_", "?", 3)
>> Testing_One_Two?Three
【讨论】:
【参考方案14】:可能是最短且最简单的解决方案之一,无需任何外部库。
def replace_nth(sub,repl,txt,nth):
arr=txt.split(sub)
part1=sub.join(arr[:nth])
part2=sub.join(arr[nth:])
return part1+repl+part2
我做了几个测试,效果很好。
【讨论】:
【参考方案15】:@Padraic Cunningham 的回答中有一个简单的错误,即出现次数 n 仅比允许的值大 1 (n = maximum_occurances + 1)。
所以这是他的代码的更正版本:
def nth_repl(s, old, new, n):
find = s.find(old)
# If find is not -1 we have found at least one match for the substring
i = find != -1
# loop until we find the nth or we find no match
while find != -1 and i != n:
# find + 1 means we start searching from after the last match
find = s.find(old, find + 1)
i += 1
# If i is equal to n we found nth match so replace
if i == n and i <= len(s.split(old))-1:
return s[:find] + new + s[find+len(old):]
return s
【讨论】:
以上是关于替换字符串中第 n 次出现的子字符串的主要内容,如果未能解决你的问题,请参考以下文章