Python regex menemukan semua kecocokan yang tumpang tindih?

101

Saya mencoba menemukan setiap 10 digit rangkaian angka dalam rangkaian angka yang lebih besar menggunakan re di Python 2.6.

Saya dengan mudah dapat meraih tidak ada pertandingan yang tumpang tindih, tetapi saya ingin setiap pertandingan dalam seri nomor. Misalnya.

di "123456789123456789"

Saya harus mendapatkan daftar berikut:

[1234567891,2345678912,3456789123,4567891234,5678912345,6789123456,7891234567,8912345678,9123456789]

Saya telah menemukan referensi ke "lookahead", tetapi contoh yang saya lihat hanya menunjukkan pasangan angka daripada pengelompokan yang lebih besar dan saya belum dapat mengubahnya melebihi dua digit.

danspants
sumber
6
Solusi yang disajikan tidak akan berfungsi ketika kecocokan yang tumpang tindih dimulai pada titik yang sama, misalnya, mencocokkan "a | ab | abc" dengan "abcd" hanya akan mengembalikan satu hasil. Apakah ada solusi untuk itu yang tidak melibatkan pemanggilan match () beberapa kali, secara manual melacak batas 'akhir'?
Vítor De Araújo
@ VítorDeAraújo: ekspresi reguler yang tumpang tindih seperti (a|ab|abc)umumnya dapat ditulis ulang sebagai ekspresi yang tidak tumpang tindih dengan grup tangkapan bersarang, misalnya (a(b(c)?)?)?, di mana kami mengabaikan semua kecuali grup tangkapan terluar (yaitu paling kiri) saat membongkar sebuah pertandingan; memang ini sedikit menyakitkan dan kurang terbaca. Ini juga akan menjadi regex yang lebih berkinerja untuk dicocokkan.
smci

Jawaban:

179

Gunakan grup penangkap di dalam lookahead. Lookahead menangkap teks yang Anda minati, tetapi kecocokan sebenarnya secara teknis adalah substring dengan lebar nol sebelum lookahead, sehingga secara teknis kecocokan tidak tumpang tindih:

import re 
s = "123456789123456789"
matches = re.finditer(r'(?=(\d{10}))',s)
results = [int(match.group(1)) for match in matches]
# results: 
# [1234567891,
#  2345678912,
#  3456789123,
#  4567891234,
#  5678912345,
#  6789123456,
#  7891234567,
#  8912345678,
#  9123456789]
daging_mekanis
sumber
2
Jawaban saya setidaknya 2 kali lebih cepat dari yang ini. Tapi solusi ini rumit, saya suka.
eyquem
16
Penjelasan = alih-alih mencari pola (10 digit), ia mencari apa saja yang DIIKUTI OLEH pola tersebut. Jadi ia menemukan posisi 0 dari string, posisi 1 dari string, dan seterusnya. Kemudian ia mengambil grup (1) - pola yang cocok dan membuat daftarnya. Sangat keren.
Tal Weiss
10
Saya bergabung dengan StackOverflow, menjawab pertanyaan, dan meningkatkan reputasi saya agar saya dapat memberikan suara positif pada jawaban ini. Saya terjebak dengan Python 2.4 untuk saat ini jadi saya tidak dapat menggunakan fungsi regex yang lebih maju dari Python 3, dan ini hanya tipuan aneh yang saya cari.
TheSoundDefense
2
Bisakah Anda menambahkan lebih banyak penjelasan ke kode. Ini bukan cara terbaik sesuai Stack Overflow, hanya memiliki kode dalam sebuah jawaban. Ini pasti akan membantu orang.
Akshay Hazari
1
Ini sangat membantu. Terima kasih: D
Sreekiran
80

Anda juga dapat mencoba menggunakan modul pihak ketigaregex (bukan re), yang mendukung pencocokan tumpang tindih.

>>> import regex as re
>>> s = "123456789123456789"
>>> matches = re.findall(r'\d{10}', s, overlapped=True)
>>> for match in matches: print(match)  # print match
...
1234567891
2345678912
3456789123
4567891234
5678912345
6789123456
7891234567
8912345678
9123456789
David C
sumber
Saya mendapatkan:TypeError: findall() got an unexpected keyword argument 'overlapped'
Carsten
@ Carsten: Anda harus menginstal regexmodul terlebih dahulu:pip install regex
David C
Itu berhasil, terima kasih. Saya mengira saya akan mendapatkan kesalahan impor jika regex tidak diinstal
Carsten
17

Saya menyukai ekspresi reguler, tetapi tidak diperlukan di sini.

Secara sederhana

s =  "123456789123456789"

n = 10
li = [ s[i:i+n] for i in xrange(len(s)-n+1) ]
print '\n'.join(li)

hasil

1234567891
2345678912
3456789123
4567891234
5678912345
6789123456
7891234567
8912345678
9123456789
eyquem
sumber
10
Regex hanya tidak diperlukan di sini karena Anda menerapkan pengetahuan khusus "dalam rangkaian angka yang lebih besar", jadi Anda sudah tahu bahwa setiap posisi 0 <= i < len(s)-n+1dijamin menjadi awal dari pertandingan 10 digit. Juga saya pikir kode Anda dapat dipercepat, akan menarik untuk kode-golf untuk kecepatan.
smci