Sepertinya ini cukup sepele, tetapi saya baru di Python dan ingin melakukannya dengan cara yang paling Pythonic.
Saya ingin mencari indeks yang sesuai dengan kejadian n'th dari substring dalam sebuah string.
Pasti ada sesuatu yang setara dengan apa yang AKU INGIN lakukan yaitu
mystring.find("substring", 2nd)
Bagaimana Anda bisa mencapai ini dengan Python?
Jawaban:
Pendekatan berulang Mark akan menjadi cara yang biasa, saya pikir.
Berikut adalah alternatif dengan pemisahan string, yang sering kali berguna untuk menemukan proses terkait:
Dan inilah cara cepat (dan agak kotor, karena Anda harus memilih sekam yang tidak bisa cocok dengan jarum) satu baris:
sumber
.rfind('XXX')
, tetapi itu akan berantakan jika'XXX'
muncul nanti di masukan.Berikut adalah versi yang lebih Pythonic dari solusi iteratif langsung:
Contoh:
Jika Anda ingin menemukan kejadian tumpang tindih ke-n
needle
, Anda dapat menambahnya1
alih-alihlen(needle)
, seperti ini:Contoh:
Ini lebih mudah dibaca daripada versi Mark, dan tidak memerlukan memori tambahan dari versi pemisahan atau pengimporan modul ekspresi reguler. Itu juga mematuhi beberapa aturan di Zen of python , tidak seperti berbagai
re
pendekatan:sumber
Ini akan menemukan kemunculan kedua substring dalam string.
Sunting: Saya belum terlalu memikirkan kinerjanya, tetapi rekursi cepat dapat membantu menemukan kejadian ke-n:
sumber
n
substring lebih sedikit daripada kejadian. (Dalam hal ini nilai pengembalian akan berputar secara berkala melalui semua posisi kejadian).Memahami bahwa regex tidak selalu merupakan solusi terbaik, saya mungkin akan menggunakannya di sini:
sumber
(m.start() for m in re.finditer(r"ab",s))[2]
itertools.islice
fungsi:next(islice(re.finditer(r"ab",s), 2, 2+1)).start()
Saya menawarkan beberapa hasil pembandingan yang membandingkan pendekatan paling menonjol yang disajikan sejauh ini, yaitu @ bobince
findnth()
(berdasarkanstr.split()
) vs. @ tgamblin atau @Mark Byers 'find_nth()
(berdasarkanstr.find()
). Saya juga akan membandingkan dengan ekstensi C (_find_nth.so
) untuk melihat seberapa cepat kita bisa pergi. Ini diafind_nth.py
:Tentu saja, kinerja paling penting jika stringnya besar, jadi misalkan kita ingin mencari baris baru ke-1000001 ('\ n') dalam file 1,3 GB yang disebut 'bigfile'. Untuk menghemat memori, kami ingin mengerjakan
mmap.mmap
representasi objek dari file:Sudah ada masalah pertama dengan
findnth()
, karenammap.mmap
objek tidak mendukungsplit()
. Jadi kami sebenarnya harus menyalin seluruh file ke dalam memori:Aduh! Untungnya
s
masih muat di memori 4 GB Macbook Air saya, jadi mari benchmarkfindnth()
:Performa yang jelas mengerikan. Mari kita lihat bagaimana pendekatan berdasarkan
str.find()
itu:Jauh lebih baik! Jelas,
findnth()
masalahnya adalah bahwa string dipaksa untuk menyalin selamasplit()
, yang sudah kedua kalinya kami menyalin 1,3 GB data setelahnyas = mm[:]
. Inilah keuntungan kedua darifind_nth()
: Kita dapat menggunakannya secaramm
langsung, sehingga tidak ada salinan file yang diperlukan:Tampaknya ada hukuman kinerja kecil yang beroperasi pada
mm
vs.s
, tetapi ini menggambarkan bahwafind_nth()
dapat memberi kita jawaban dalam 1,2 d dibandingkan denganfindnth
total 47 d.Saya tidak menemukan kasus di mana
str.find()
pendekatan berbasis secara signifikan lebih buruk daripadastr.split()
pendekatan berbasis, jadi pada titik ini, saya berpendapat bahwa jawaban @ tgamblin atau @Mark Byers harus diterima daripada @ bobince.Dalam pengujian saya, versi di
find_nth()
atas adalah solusi Python murni tercepat yang dapat saya buat (sangat mirip dengan versi @Mark Byers). Mari kita lihat seberapa baik yang bisa kita lakukan dengan modul ekstensi C. Ini dia_find_nthmodule.c
:Ini
setup.py
filenya:Instal seperti biasa dengan
python setup.py install
. Kode C memainkan keuntungan di sini karena terbatas pada menemukan karakter tunggal, tetapi mari kita lihat seberapa cepat ini:Jelas masih lebih cepat. Menariknya, tidak ada perbedaan pada level C antara in-memory dan case mmapped. Hal ini juga menarik untuk melihat bahwa
_find_nth2()
, yang didasarkan padastring.h
'smemchr()
fungsi perpustakaan, kehilangan menentang pelaksanaan langsung di_find_nth()
: The tambahan 'optimasi' dimemchr()
rupanya knalpot ...Kesimpulannya, implementasi dalam
findnth()
(berdasarkanstr.split()
) benar-benar ide yang buruk, karena (a) ia bekerja sangat buruk untuk string yang lebih besar karena penyalinan yang diperlukan, dan (b) tidak bekerja padammap.mmap
objek sama sekali. Penerapan dalamfind_nth()
(berdasarkanstr.find()
) harus diutamakan dalam semua keadaan (dan karena itu menjadi jawaban yang diterima untuk pertanyaan ini).Masih ada sedikit ruang untuk perbaikan, karena ekstensi C berjalan hampir 4 kali lipat lebih cepat daripada kode Python murni, menunjukkan bahwa mungkin ada kasus untuk fungsi pustaka Python khusus.
sumber
Cara paling sederhana?
sumber
Saya mungkin akan melakukan sesuatu seperti ini, menggunakan fungsi find yang mengambil parameter indeks:
Kurasa tidak terlalu Pythonic, tapi sederhana. Anda dapat melakukannya dengan menggunakan rekursi:
Ini cara fungsional untuk mengatasinya, tapi saya tidak tahu apakah itu membuatnya lebih Pythonic.
sumber
for _ in xrange(n):
dapat digunakan sebagai penggantiwhile n: ... n-=1
return find_nth(s, x, n - 1, i + 1)
seharusnyareturn find_nth(s, x, n - 1, i + len(x))
. Bukan masalah besar, tetapi menghemat waktu komputasi.Ini akan memberi Anda larik indeks awal untuk kecocokan dengan
yourstring
:Maka entri ke-n Anda adalah:
Tentu saja Anda harus berhati-hati dengan batas indeks. Anda bisa mendapatkan jumlah contoh
yourstring
seperti ini:sumber
Berikut adalah pendekatan lain menggunakan re.finditer.
Perbedaannya adalah bahwa ini hanya melihat tumpukan jerami sejauh yang diperlukan
sumber
Berikut
re
+itertools
versi lain yang seharusnya berfungsi saat menelusuri astr
atau aRegexpObject
. Saya akan dengan bebas mengakui bahwa ini kemungkinan besar direkayasa, tetapi untuk beberapa alasan itu menghibur saya.sumber
Membangun dari jawaban modle13 , tetapi tanpa
re
ketergantungan modul.Saya agak berharap ini adalah metode string bawaan.
sumber
sumber
Memberikan solusi lain yang "rumit", yang menggunakan
split
danjoin
.Dalam contoh Anda, kami dapat menggunakan
sumber
sumber
find_nth('aaa', 'a', 0)
kembali1
saat seharusnya kembali0
. Anda membutuhkan sesuatu sepertii = s.find(substr, i) + 1
dan kemudian kembalii - 1
.Solusi tanpa menggunakan loop dan rekursi.
sumber
Untuk kasus khusus di mana Anda mencari kemunculan n'th dari sebuah karakter (yaitu substring dengan panjang 1), fungsi berikut bekerja dengan membuat daftar dari semua posisi kemunculan karakter yang diberikan:
Jika ada lebih sedikit dari
n
kemunculan karakter yang diberikan, itu akan memberiIndexError: list index out of range
.Ini berasal dari jawaban @ Zv_oDD dan disederhanakan untuk kasus satu karakter.
sumber
Ganti satu liner bagus tetapi hanya berfungsi karena XX dan bar memiliki lentgh yang sama
Definisi yang baik dan umum adalah:
sumber
Inilah jawaban yang sangat Anda inginkan:
sumber
Inilah solusi saya untuk menemukan
n
kemunculanb
dalam stringa
:Ini adalah Python murni dan berulang. Untuk 0 atau
n
yang terlalu besar, ia mengembalikan -1. Ini adalah satu baris dan dapat digunakan secara langsung. Berikut ini contohnya:sumber
Def:
Menggunakan:
Keluaran:
sumber
Bagaimana tentang:
sumber