问题
I want to replace the n'th occurrence of a substring in a string.
There's got to be something equivalent to what I WANT to do which is
mystring.replace("substring", 2nd)
What is the simplest and most Pythonic way to achieve this?
Why not duplicate: I don't want to use regex for this approach and most of answers to similar questions I found are just regex stripping or really complex function. I really want as simple as possible and not regex solution.
回答1:
You can use a while loop with str.find
to find the nth occurrence if it exists and use that position to create the new string:
def nth_repl(s, sub, repl, nth):
find = s.find(sub)
# if find is not p1 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 != nth:
# find + 1 means we start at the last match start index + 1
find = s.find(sub, find + 1)
i += 1
# if i is equal to nth we found nth matches so replace
if i == nth:
return s[:find]+repl+s[find + len(sub):]
return s
Example:
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'
回答2:
I use simple function, which lists all occurrences, picks the nth one's position and uses it to split original string into two substrings. Then it replaces first occurrence in the second substring and joins substrings back into the new string:
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
For these variables:
string = 'ababababababababab'
sub = 'ab'
wanted = 'CD'
n = 5
outputs:
ababababCDabababab
Notes:
The
where
variable actually is a list of matches' positions, where you pick up the nth one. But list item index starts with0
usually, not with1
. Therefore there is an-1
index andn
variable is the actual nth substring. My example finds 5th string. If you usen
index and want to find 5th position, you'll needn
to be4
. Which you use usually depends on the function, which generates ourn
.This should be the simplest way, but maybe it isn't the most Pythonic way, because the
where
variable construction needs importingre
library. Maybe somebody will find even more Pythonic way.Sources and some links in addition:
where
construction: Find all occurrences of a substring in Python- string splitting: https://www.daniweb.com/programming/software-development/threads/452362/replace-nth-occurrence-of-any-sub-string-in-a-string
- similar question: Find the nth occurrence of substring in a string
回答3:
I have come up with the below, which considers also options to replace all 'old' string occurrences to the left or to the right. Naturally, there is no option to replace all occurrences, as standard str.replace works perfect.
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:
The last answer is nearly perfect - only one correction:
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
The after-string has to be stored in the this variable again after replacement. Thank you for the great solution!
回答5:
I had a similar need, i.e to find the IPs in logs and replace only src IP or dst IP field selectively. This is how i achieved in a pythonic way;
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)
回答6:
def replace_nth_occurance(some_str, original, replacement, n):
""" Replace nth occurance of a string with another string
"""
some_str.replace(original, replacement, n)
for i in range(n):
some_str.replace(replacement, original, i)
return some_str
回答7:
I've tweaked @aleskva's answer to better work with regex and wildcards:
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)
This gives abdsahd15jhdasjk124ndjaksnd124ndjkas
. Note the use of ?
to make the query non-greedy.
I realise that the question explicitly states that they didn't want to use regex, however it may be useful to be able to use wildcards in a clear fashion (hence my answer).
来源:https://stackoverflow.com/questions/35091557/replace-nth-occurrence-of-substring-in-string