web-dev-qa-db-ja.com

文字列内のn番目の部分文字列を置換

文字列内のn番目の部分文字列を置き換えたいのですが。

私がやりたいことと同等のものがあるはずです

mystring.replace("substring", 2nd)

これを実現する最も簡単で最もPython的な方法は何ですか?

重複しない理由:このアプローチに正規表現を使用したくないので、同様の質問に対するほとんどの答えは、正規表現の除去または本当に複雑な関数です。私は本当に正規表現の解決策ではなく、可能な限りシンプルなものを求めています。

18
aleskva

すべてのオカレンスをリストし、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番目の位置を検索する場合は、n4にする必要があります。通常、どちらを使用するかは、nを生成する関数によって異なります。

これは最も簡単な方法ですが、where変数の構築にはreライブラリをインポートする必要があるため、最もPython的な方法ではない可能性があります。たぶん誰かがもっともっとPython的な方法を見つけるでしょう。

ソースといくつかのリンクに加えて:

5
aleskva

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'
19

私は以下を思いつきました、それはまたすべての「古い」文字列の出現を左または右に置き換えるオプションも考慮します。当然、標準の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)
2
CapedHero

最後の答えはほぼ完璧です-唯一の修正:

    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変数に再度格納する必要があります。素晴らしい解決策をありがとう!

1
Victor.Bbb

正規表現とワイルドカードをより適切に処理するために、@ 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が得られます。クエリを貪欲にしないために?を使用していることに注意してください。

私は質問が正規表現を使用したくないことを明示的に述べていることを理解していますが、明確な方法でワイルドカードを使用できると便利な場合があります(そのため、私の答え)。

1
J.Warren
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
0
vineeshvs

同様のニーズがありました。つまり、ログで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)
0
Indu Sharma