Mari kita mengatakan bahwa saya memiliki 10.000 regexes dan satu string dan saya ingin mengetahui apakah string pertandingan mereka dan mendapatkan semua pertandingan. Sepele cara untuk melakukannya akan hanya query string satu per satu terhadap semua regexes. Ada yang lebih cepat,lebih efisien cara untuk melakukannya?
EDIT: Saya telah mencoba mengganti dengan DFA's (lex) Masalahnya di sini adalah bahwa hal itu hanya akan memberikan satu pola tunggal. Jika aku punya string "hello" dan pola "[H|h]ello" dan ".{0,20}ello", DFA hanya akan cocok dengan salah satu dari mereka, tapi aku ingin mereka berdua untuk memukul.
I've menemukan masalah yang sama di masa lalu. Saya menggunakan solusi yang sama untuk salah satu yang disarankan oleh akdom.
Saya beruntung bahwa saya ekspresi reguler biasanya memiliki beberapa substring yang harus muncul dalam setiap string yang cocok. Saya mampu untuk mengambil substring sederhana menggunakan parser dan indeks mereka di FSA menggunakan Aho-Corasick algoritma. Indeks ini kemudian digunakan untuk dengan cepat menghilangkan semua ekspresi reguler yang sepele don't pertandingan string tertentu, hanya menyisakan beberapa ekspresi reguler untuk memeriksa.
Saya merilis kode di bawah LGPL sebagai Python/C modul. Lihat esmre di Google code hosting.
Kami harus melakukan ini pada produk yang saya kerjakan sekali. Jawabannya adalah untuk mengkompilasi semua regexes bersama-sama ke Deterministic Finite State Machine (juga dikenal sebagai deterministic finite automaton atau DFA). Dalam DFA kemudian bisa berjalan karakter demi karakter dari string anda dan akan api "pertandingan" event setiap kali salah satu ungkapan yang cocok.
Kelebihan apakah itu berjalan cepat (setiap karakter yang dibandingkan hanya sekali) dan tidak mendapatkan apapun lebih lambat jika anda menambahkan lebih banyak ekspresi.
Kekurangan ini adalah bahwa hal itu memerlukan besar tabel data untuk robot, dan ada banyak jenis ekspresi reguler yang tidak didukung (misalnya, back-referensi).
Salah satu yang kita digunakan adalah tangan-kode oleh C++ template kacang di perusahaan kami pada saat itu, begitu sayangnya aku don't memiliki FOSS solusi untuk mengarahkan anda menuju. Tapi jika anda google regex atau ekspresi reguler dengan "DFA" anda'll menemukan barang-barang yang akan mengarahkan anda dalam arah yang benar.
Ini adalah cara lexers bekerja.
Ekspresi reguler dikonversi ke satu non-deterministik automata (NFA) dan mungkin berubah dalam deterministik automata (DFA).
Yang dihasilkan robot akan mencoba untuk mencocokkan semua ekspresi reguler sekaligus dan akan berhasil pada salah satu dari mereka.
Ada banyak alat yang dapat membantu anda di sini, mereka disebut "lexer generator" dan ada solusi yang bekerja dengan sebagian besar bahasa.
Anda don't mengatakan bahasa yang sedang anda gunakan. Untuk C programmers saya akan menyarankan untuk melihat-lihat di re2c tool. Tentu saja tradisional (f)lex adalah selalu pilihan.
Martin Sulzmann Telah melakukan cukup banyak pekerjaan di bidang ini. Ia memiliki a HackageDB proyek menjelaskan breifly di sini yang menggunakan turunan parsial tampaknya dibuat khusus untuk ini.
Bahasa yang digunakan adalah Haskell dan dengan demikian akan sangat sulit untuk menerjemahkan ke non fungsional bahasa jika itu adalah keinginan (saya akan berpikir terjemahan untuk banyak bahasa FP masih akan cukup sulit).
Kode ini tidak didasarkan pada konversi untuk serangkaian automata dan kemudian menggabungkan mereka, melainkan didasarkan pada manipulasi simbolis dari regexes diri mereka sendiri.
Juga kode ini sangat banyak eksperimen dan Martin tidak lagi seorang profesor tapi di 'pekerjaan yang menguntungkan'(1) jadi mungkin tidak tertarik/tidak mampu untuk menyediakan bantuan atau masukan.
10,000 regexen eh? Eric Wendelin's saran dari hirarki tampaknya menjadi ide yang baik. Pernahkah anda berpikir untuk mengurangi besarnya ini regexen untuk sesuatu seperti struktur pohon?
Sebagai contoh sederhana: Semua regexen membutuhkan nomor bisa lepas dari salah satu regex memeriksa demikian, semua regexen tidak membutuhkan satu ke cabang yang lain. Dalam mode ini anda bisa mengurangi jumlah aktual perbandingan turun ke jalan bersama pohon bukannya melakukan setiap perbandingan dalam 10.000.
Ini akan membutuhkan membusuk regexen diberikan ke genre, masing-masing genre memiliki ruang tes yang akan memerintah mereka jika gagal. Dengan cara ini anda secara teoritis bisa mengurangi jumlah aktual perbandingan secara dramatis.
Jika anda harus melakukan hal ini pada waktu berjalan anda bisa mengurai melalui anda diberikan ekspresi reguler dan "file" mereka menjadi salah satu genre yang telah ditetapkan (paling mudah untuk dilakukan) atau komparatif genre yang dihasilkan pada saat itu (tidak mudah dilakukan).
Misalnya anda membandingkan "hello" untuk "[H|h]ello" dan ".{0,20}ello" tidak't benar-benar dapat membantu dengan solusi ini. Kasus sederhana di mana ini bisa berguna akan sama: jika anda memiliki 1000 tes yang hanya akan mengembalikan true jika "katanya" ada di suatu tempat dalam string dan string tes adalah "selamat tinggal;" anda hanya harus melakukan satu tes pada "katanya" dan tahu bahwa 1000 memerlukan tes ini tidak't bekerja, dan karena ini, anda tidak't harus melakukannya.
Jika anda're berpikir dalam istilah "10,000 regexes" anda perlu untuk mengalihkan anda melalui proses. Jika tidak ada yang lain, berpikir dalam hal "10,000 target string untuk pertandingan". Kemudian mencari non-regex metode yang dibangun untuk berurusan dengan "muatan kapal target string" situasi, seperti Aho-Corasick mesin. Terus terang, meskipun, tampaknya seperti ada sesuatu yang hilang dari rel jauh lebih awal dalam proses dari mesin yang digunakan, sejak 10.000 string target suara yang lebih banyak seperti database lookup dari string pertandingan.
Anda'a harus memiliki beberapa cara untuk menentukan jika diberikan regex adalah "zat aditif" dibandingkan dengan yang lain. Membuat regex "hirarki" dari jenis yang memungkinkan anda untuk menentukan bahwa semua regexs dari cabang tertentu yang tidak sesuai
Anda bisa menggabungkan mereka dalam kelompok mungkin 20.
(?=(regex1)?)(?=(regex2)?)(?=(regex3)?)...(?=(regex20)?)
Asalkan masing-masing regex memiliki nol (atau setidaknya jumlah yang sama) menangkap kelompok, anda dapat melihat apa yang ditangkap untuk melihat pola(s) cocok.
Jika regex1 cocok, menangkap kelompok 1 akan memiliki itu's sesuai teks. Jika tidak, itu akan menjadi undefined
/Tidak ada
/null
/...
Aho-Corasick adalah jawaban bagi saya.
Aku punya 2000 kategori hal-hal yang masing-masing memiliki daftar pola untuk pertandingan melawan. String suhu udara rata-rata sekitar 100.000 karakter.
Utama Peringatan: The patters untuk mencocokkan semua bahasa patters tidak pola regex misal 'kucing'
vs r'\w+'
.
Saya menggunakan python sehingga digunakan https://pypi.python.org/pypi/pyahocorasick/.
import ahocorasick
A = ahocorasick.Automaton()
patterns = [
[['cat','dog'],'mammals'],
[['bass','tuna','trout'],'fish'],
[['toad','crocodile'],'amphibians'],
]
for row in patterns:
vals = row[0]
for val in vals:
A.add_word(val, (row[1], val))
A.make_automaton()
_string = 'tom loves lions tigers cats and bass'
def test():
vals = []
for item in A.iter(_string):
vals.append(item)
return vals
Menjalankan %waktuhal test()
pada tahun 2000 kategori dengan sekitar 2-3 jejak per kategori dan _string
panjang sekitar 100,000
punya saya 2.09 ms
vs 631 ms
melakukan berurutan kembali.pencarian()
315x lebih cepat!.
Jika anda're menggunakan ekspresi reguler (orang-orang yang sesuai untuk bahasa regular dari teori bahasa formal, dan tidak beberapa Perl-seperti non-regular hal), kemudian anda're beruntung, karena biasa bahasa yang tertutup di bawah union. Dalam kebanyakan regex bahasa, pipa (|) adalah union. Jadi anda harus mampu untuk membangun sebuah string (mewakili ekspresi reguler anda inginkan) sebagai berikut:
(r1)|(r2)|(r3)|...|(r10000)
di mana tanda kurung untuk mengelompokkan, tidak cocok. Apa-apa yang sesuai dengan ekspresi reguler yang cocok dengan setidaknya salah satu dari anda asli ekspresi reguler.
I'd mengatakan bahwa itu's pekerjaan untuk real parser. Titik tengah mungkin Parsing Ekspresi Tata bahasa (PEG). It's lebih tinggi-tingkat abstraksi dari pencocokan pola, salah satu fitur adalah bahwa anda dapat menentukan keseluruhan tata bahasa bukan dari pola tunggal. Ada beberapa high-performance implementasi yang bekerja dengan menyusun tata bahasa anda ke dalam bytecode dan berjalan dalam berupa VM.
disclaimer: satu-satunya yang saya tahu adalah LPEG, perpustakaan untuk Lua, dan itu bukan't mudah (bagi saya) untuk memahami dasar konsep-konsep.
Anda bisa mengkompail ekspresi reguler ke hybrid DFA/Bucchi automata di mana setiap kali BA memasuki menerima keadaan anda bendera yang regex aturan "tekan".
Bucchi adalah sedikit berlebihan untuk hal ini, tapi mengubah cara anda DFA bekerja bisa melakukan trik.
Saya akan merekomendasikan menggunakan Intel's Hyperscan jika semua yang anda butuhkan adalah untuk mengetahui ekspresi reguler pertandingan. Hal ini dibangun untuk tujuan ini. Jika tindakan yang anda butuhkan untuk mengambil yang lebih canggih, anda juga dapat menggunakan rageldari. Meskipun menghasilkan satu DFA dan dapat mengakibatkan banyak negara, dan akibatnya sangat besar eksekusi program. Hyperscan mengambil hybrid NFA/DFA/custom pendekatan pencocokan yang menangani jumlah besar dari ekspresi dengan baik.
Saya menggunakan Rageldari dengan meninggalkan tindakan:
action hello {...}
action ello {...}
action ello2 {...}
main := /[Hh]ello/ % hello |
/.+ello/ % ello |
any{0,20} "ello" % ello2 ;
String "hello" sebut kode dalam aksi halo
block, maka dalam aksi ello
blok dan terakhir di aksi ello2
blok.
Mereka ekspresi reguler sangat terbatas dan bahasa mesin lebih disukai sebaliknya, kawat gigi dari contoh anda hanya bekerja dengan bahasa yang lebih umum.
Saya berpikir bahwa jawaban singkatnya adalah bahwa ya, ada cara untuk melakukan hal ini, dan bahwa hal ini juga diketahui untuk ilmu komputer, dan bahwa saya dapat't ingat apa itu.
Jawaban singkatnya adalah bahwa anda mungkin menemukan bahwa anda regex penerjemah sudah berurusan dengan semua ini secara efisien ketika |'d bersama-sama, atau mungkin anda menemukan satu yang tidak. Jika tidak, itu's waktu bagi anda untuk google string-matching dan algoritma pencarian.
Cara tercepat untuk melakukan itu tampaknya menjadi sesuatu seperti ini (kode C#):
public static List<Regex> FindAllMatches(string s, List<Regex> regexes)
{
List<Regex> matches = new List<Regex>();
foreach (Regex r in regexes)
{
if (r.IsMatch(string))
{
matches.Add(r);
}
}
return matches;
}
Oh, anda berarti tercepat kode? saya don't tahu kemudian....