文字列内のn番目の部分文字列を置き換えたいのですが。
私がやりたいことと同等のものがあるはずです
mystring.replace("substring", 2nd)
これを実現する最も簡単で最もPython的な方法は何ですか?
重複しない理由:このアプローチに正規表現を使用したくないので、同様の質問に対するほとんどの答えは、正規表現の除去または本当に複雑な関数です。私は本当に正規表現の解決策ではなく、可能な限りシンプルなものを求めています。
すべてのオカレンスをリストし、n番目の位置を選択し、それを使用して元のストリングを2つのサブストリングに分割する単純な関数を使用します。次に、2番目の部分文字列の最初の出現を置き換え、部分文字列を結合して新しい文字列に戻します。
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
を生成する関数によって異なります。これは最も簡単な方法ですが、
where
変数の構築にはre
ライブラリをインポートする必要があるため、最もPython的な方法ではない可能性があります。たぶん誰かがもっともっとPython的な方法を見つけるでしょう。ソースといくつかのリンクに加えて:
str.find
でwhileループを使用して、n番目のオカレンスが存在する場合はそれを検索し、その位置を使用して新しい文字列を作成できます。
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
例:
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'
私は以下を思いつきました、それはまたすべての「古い」文字列の出現を左または右に置き換えるオプションも考慮します。当然、標準の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)
最後の答えはほぼ完璧です-唯一の修正:
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
置換後、文字列をthis変数に再度格納する必要があります。素晴らしい解決策をありがとう!
正規表現とワイルドカードをより適切に処理するために、@ 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
が得られます。クエリを貪欲にしないために?
を使用していることに注意してください。
私は質問が正規表現を使用したくないことを明示的に述べていることを理解していますが、明確な方法でワイルドカードを使用できると便利な場合があります(そのため、私の答え)。
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
同様のニーズがありました。つまり、ログで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)