質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
置換

置換とは文字列中の特定の文字に対して、別の文字列に置き換えることを指します。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

6回答

10306閲覧

pythonでの文字列置換で、n番目の一致のみ対象としたい

jammer

総合スコア14

置換

置換とは文字列中の特定の文字に対して、別の文字列に置き換えることを指します。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2019/11/13 09:24

前提・実現したいこと

pythonで文字列置換する際、文字列に含まれる対象が複数含まれたときに、
n番目の一致のみを、置換したい。

発生している問題・エラーメッセージ

すべて置換する方法か、n番目「まで」を置換する方法しか分からず、
遠回りの方法しか思いついていない。

該当のソースコード

現在のソースコードです。
sample変数の文字列に含まれる「XXX」のうち、
2番目に出現するもののみを、「xxx」に置換します。

  1. n-1番目までの一致を一時的に「TEMP」として置換
  2. 1.に対し、1番目の一致を置換
  3. 2.に対し、「TEMP」としたものをもとの文字に戻す置換

という処理をしており、回りくどい気がしています。

python

1sample = 'abcXXXdefXXXghiXXXjkl' #置換する対象の文字列 2 3n = 2 #この番目に一致したもののみ置換したい 4b = 'XXX' #置換前 5a = 'xxx' #置換後 6t = '_TEMP_' #一時変換用の意味のない文字列 7 8result = sample.replace(b,t,n-1).replace(b,a,1).replace(t,b,n-1) 9print(result) 10 11# abcXXXdefxxxghiXXXjkl

もっとスマートに置換する方法があれば教えてください。

補足情報(FW/ツールのバージョンなど)

python3.7

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答6

0

ぱっと思い付いた方法です。もっと良いやり方があるかもしれません。

目的の文字列で分割する方法

Python

1sample = 'abcXXXdefXXXghiXXXjkl' 2f = 'XXX' 3t = '---' 4 5for n in range(1, 5): 6 parts = sample.split(f) 7 if n < len(parts): 8 dst = f.join(parts[:n]) + t + f.join(parts[n:]) 9 else: 10 dst = sample 11 12 print(dst)

実行結果 Wandbox

plain

1abc---defXXXghiXXXjkl 2abcXXXdef---ghiXXXjkl 3abcXXXdefXXXghi---jkl 4abcXXXdefXXXghiXXXjkl

str.findでゴリ押す方法

Python

1sample = 'abcXXXdefXXXghiXXXjkl' 2f = 'XXX' 3t = '---' 4 5for n in range(1, 5): 6 pos = -1 7 for i in range(n): 8 pos = sample.find(f, pos+1) 9 10 dst = sample[:pos] + sample[pos:].replace(f, t, 1) 11 print(dst)

実行結果 Wandbox

plain

1abc---defXXXghiXXXjkl 2abcXXXdef---ghiXXXjkl 3abcXXXdefXXXghi---jkl 4abcXXXdefXXXghiXXXjkl

泥臭いですけれど、関数に押し込めば充分使えそうです。
どちらの方法も、文字列の仮置きを介さないという意味ではご提示の方法に勝ります。

投稿2019/11/13 09:39

編集2019/11/13 10:11
LouiS0616

総合スコア35660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hayataka2049

2019/11/13 12:01 編集

str.findでゴリ押す方法、かなり効率良さそうで良いですね。C拡張とか持ち出さない限りはこれが最良かも。
guest

0

ベストアンサー

効率を無視してこんな感じも

python

1 2import re 3 4sample = 'abcXXXdefXXXghiXXXjkl' #置換する対象の文字列 5 6n = 2 #この番目に一致したもののみ置換したい 7b = 'XXX' #置換前 8a = 'xxx' #置換後 9 10result = list(re.finditer(b,sample)) 11#print(result[n-1]) 12i = result[n-1].span() 13#print(i) 14print(sample[slice(*i)]) 15dest = sample[:i[0]] + sample[i[0]:].replace(b,a,1) 16print(dest)

投稿2019/11/13 10:54

t_obara

総合スコア5488

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dameo

2019/11/13 12:12

人は得てして「やっぱり固定文字列では物足りない」とか言い始めるものです。 というわけでこの方法に一票。 正規表現だってコンパイル後は結構速い(でもfindには劣ります)。 iteratorなので一杯マッチするとちょっと勿体ないけど。。。
t_obara

2019/11/14 01:30

> iteratorなので一杯マッチするとちょっと勿体 ですよねw
guest

0

正規表現で。

Python

1n=2 2result = re.sub(f'((?:XXX.*?){{{n-1}}})XXX',r'\1xxx',sample)

Python

1n=2 2a="xxx" 3b="XXX" 4result = re.sub(f'((?:{re.escape(b)}.*?){{{n-1}}}){re.escape(b)}',f'\1{a}',sample)

長い、、、か。

投稿2019/11/13 13:10

編集2019/11/14 01:06
otn

総合スコア84555

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

文字列が現れる位置をすべて列挙する方法が以下にあります。

  • How to find all occurrences of a substring?

https://stackoverflow.com/questions/4664850/

これをつかって出現位置をすべて列挙してしまえば、あとは任意の出現位置だけを置換することができるはずです。

投稿2019/11/13 22:29

編集2019/11/14 14:41
katoy

総合スコア22324

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jammer

2019/11/14 00:53

[m.start() for m in re.finditer(ptn, string)] この書き方、めちゃくちゃスッキリしてますね。 正直、私にはstart()の部分の処理が分からず、なぜこれでマッチオブジェクトから出現位置(開始位置)だけが返ってくるのかさっぱりですが・・・ t_obaraさんの回答のアプローチの別解みたいな感じになりますね。
guest

0

str.partion()というのを見つけたので使ってみました。

python

1def replace_nth(s, f, t, n): 2 # 文字列sの中のn番目のfをtに置換する 3 pre, sep, post = s.partition(f) 4 if n == 1: 5 return pre + t + post if sep else pre 6 return pre + sep + replace_nth(post, f, t, n-1)

投稿2019/11/13 12:33

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

シンプルとは言えなくなっているので、参考程度の回答です。ループではなく、再帰を利用した例です。

Python3

1# coding: utf-8 2 3def find_pos(s, n, c, si, ts): 4 pos = s.find(ts, si) 5 if pos != -1: 6 if c == n: 7 return pos 8 else: 9 return find_pos(s, n, c + 1, pos + 1, ts) 10 11 return -1 12 13 14def do_replace(s, n, f, t): 15 pos = find_pos(s, n, 1, 0, f) 16 ns = s if pos == -1 else s[:pos] + s[pos:].replace(f, t, 1) 17 return ns 18 19 20sample = 'abcXXXdefXXXghiXXXjkl' 21f = 'XXX' 22t = '---' 23for n in range(1, 5): 24 s = do_replace(sample, n, f, t) 25 print(s) 26 27

実行結果 (Windows Cygwin)

sh

1$ python3 --version 2Python 3.6.9 3 4$ python3 t1.py 5abc---defXXXghiXXXjkl 6abcXXXdef---ghiXXXjkl 7abcXXXdefXXXghi---jkl 8abcXXXdefXXXghiXXXjkl 9

投稿2019/11/13 11:11

編集2019/11/13 12:16
dodox86

総合スコア9183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問