Pertanyaan ini sangat buruk karena tidak jelas apa yang ditanyakan. Semua jawaban menafsirkannya secara berbeda. @DaveF bisa tolong jelaskan pertanyaannya?
Saya tidak setuju bahwa ekspresi reguler adalah alat yang salah untuk ini karena beberapa alasan. 1) Kebanyakan implementasi ekspresi reguler memiliki solusi yang bisa diterapkan jika tidak sempurna untuk ini. 2) Seringkali Anda mencoba untuk menemukan pasangan pembatas yang seimbang dalam konteks di mana kriteria lain yang cocok untuk ekspresi reguler juga sedang dimainkan. 3) Seringkali Anda menyerahkan ekspresi reguler ke beberapa API yang hanya menerima ekspresi reguler dan Anda tidak punya pilihan.
Regex adalah alat yang BENAR untuk pekerjaan itu. Jawaban ini tidak benar. Lihat jawaban rogal111.
Andrew
4
Sepenuhnya setuju dengan jawabannya. Meskipun ada beberapa implementasi rekursi di regexp, mereka sama dengan mesin negara-terbatas dan tidak disarankan untuk bekerja dengan struktur bersarang, tetapi Tata Bahasa Konteks Gratis melakukan ini. Lihatlah hierarki formal Tata Bahasa Homsky.
Nick Roz
138
Saya ingin menambahkan jawaban ini untuk referensi cepat. Jangan ragu untuk memperbarui.
Saat Anda mengulangi grup dengan quantifier posesif, tidak ada gunanya membuat grup itu atomik karena semua posisi mundur dalam grup itu dihapus pada setiap pengulangan. Jadi menulis (?>[^)(]+|(?R))*+sama dengan menulis (?:[^)(]+|(?R))*+. Hal yang sama untuk pola selanjutnya. Tentang versi yang belum dibuka, Anda dapat meletakkan quantifier posesif di sini: [^)(]*+untuk mencegah kemunduran (jika tidak ada braket penutup).
Casimir et Hippolyte
Tentang pola Ruby 1.9, alih-alih membuat atom grup berulang (yang memiliki minat terbatas ketika ada banyak tanda kurung bersarang (...(..)..(..)..(..)..(..)..)) dalam string subjek), Anda dapat menggunakan grup non-tangkapan sederhana dan melampirkan semuanya dalam grup atom: (?>(?:[^)(]+|\g<1>)*)( ini berperilaku persis seperti quantifier posesif). Di Ruby 2.x, kuantifier posesif tersedia.
Casimir et Hippolyte
@CasimiretHippolyte Terima kasih! Saya menyesuaikan pola PCRE dan untuk Ruby 1.9, apakah maksud Anda seluruh pola menjadi seperti ini ? Silakan memperbarui diri Anda. Saya mengerti maksud Anda, tetapi tidak yakin apakah ada banyak peningkatan.
Contoh akan sangat berguna di sini, saya tidak bisa membuat ini berfungsi untuk hal-hal seperti "(1, (2, 3)) (4, 5)".
Andy Hayden
4
@AndyHayden ini karena "(1, (2, 3)) (4, 5)" memiliki dua grup yang dipisahkan dengan ruang. Gunakan regexp saya dengan flag global: / (([^ ()] | (? R)) *) / g. Ini tes online: regex101.com/r/lF0fI1/1
NET 4.5 Saya mendapatkan error berikut untuk pola ini: Unrecognized grouping construct.
nam
3
Luar biasa! Ini adalah fitur hebat dari regex. Terima kasih telah menjadi satu-satunya yang benar-benar menjawab pertanyaan. Juga, situs regex101 itu manis.
Andrew
28
[^\(]*(\(.*\))[^\)]*
[^\(]*cocok dengan semua yang bukan braket pembuka di awal string, (\(.*\))menangkap substring yang diperlukan yang terlampir dalam tanda kurung, dan [^\)]*cocok dengan semua yang bukan braket penutup di akhir string. Perhatikan bahwa ungkapan ini tidak berusaha mencocokkan tanda kurung; pengurai sederhana (lihat jawaban dehmann ) akan lebih cocok untuk itu.
braket di dalam kelas tidak perlu diloloskan. Karena di dalamnya bukan metacharacted.
José Leal
10
Expr ini gagal terhadap sesuatu seperti "teks (teks) teks (teks) teks" kembali "(teks) teks (teks)". Ekspresi reguler tidak dapat menghitung tanda kurung.
Christian Klauser
17
(?<=\().*(?=\))
Jika Anda ingin memilih teks di antara dua tanda kurung yang cocok , Anda kurang beruntung dengan ekspresi reguler. Ini tidak mungkin (*) .
Regex ini hanya mengembalikan teks antara pembukaan pertama dan kurung tutup terakhir di string Anda.
(*) Kecuali jika mesin regex Anda memiliki fitur seperti kelompok penyeimbang atau rekursi . Jumlah mesin yang mendukung fitur-fitur tersebut perlahan-lahan bertambah, tetapi mereka masih belum tersedia secara umum.
Apa arti dari tanda-tanda "<=" dan "="? Apa mesin regexp penargetan ekspresi ini?
Christian Klauser
1
Ini adalah melihat-lihat, atau lebih tepatnya "pernyataan nol lebar melihat ke depan / melihat ke belakang". Kebanyakan mesin regex modern mendukungnya.
Tomalak
Menurut contoh OP, ia ingin memasukkan orangtua yang paling luar dalam pertandingan. Regex ini membuangnya.
Alan Moore
1
@Lan M: Anda benar. Tetapi menurut teks pertanyaan, dia menginginkan segalanya di antara orangtua yang paling luar. Pilih pilihan Anda. Dia mengatakan dia telah mencoba selama berjam-jam, jadi bahkan tidak menganggap "segala sesuatu termasuk orangtua terluar" sebagai niat, karena itu sangat sepele: "(. *)".
Tomalak
3
@ ghayes Jawabannya adalah dari 2009. Itu sudah lama sekali; mesin ekspresi reguler yang memungkinkan beberapa bentuk rekursi lebih jarang daripada sekarang (dan mereka masih sangat jarang). Saya akan menyebutkannya dalam jawaban saya.
Tomalak
14
Jawaban ini menjelaskan batasan teoretis mengapa ekspresi reguler bukan alat yang tepat untuk tugas ini.
Ekspresi reguler tidak dapat melakukan ini.
Ekspresi reguler didasarkan pada model komputasi yang dikenal sebagai Finite State Automata (FSA). Seperti namanya, a FSAhanya dapat mengingat keadaan saat ini, ia tidak memiliki informasi tentang keadaan sebelumnya.
Dalam diagram di atas, S1 dan S2 adalah dua negara di mana S1 adalah langkah awal dan akhir. Jadi jika kita mencoba dengan string 0110, transisi berjalan sebagai berikut:
0110-> S1 -> S2 -> S2 -> S2 ->S1
Dalam langkah di atas, ketika kita berada di kedua S2yaitu setelah parsing 01dari 0110, FSA tidak memiliki informasi tentang sebelumnya 0di 01karena hanya bisa mengingat keadaan saat ini dan simbol input berikutnya.
Dalam masalah di atas, kita perlu mengetahui no dari tanda kurung buka; ini berarti harus disimpan di suatu tempat. Tetapi karena FSAstidak bisa melakukan itu, ekspresi reguler tidak dapat ditulis.
Namun, suatu algoritma dapat ditulis untuk melakukan tugas ini. Algoritma umumnya jatuh di bawah Pushdown Automata (PDA). PDAadalah satu tingkat di atas FSA. PDA memiliki tumpukan tambahan untuk menyimpan beberapa informasi tambahan. PDA dapat digunakan untuk memecahkan masalah di atas, karena kita dapat ' push' membuka tanda kurung di tumpukan dan ' pop' mereka begitu kita menemukan tanda kurung penutup. Jika pada akhirnya, tumpukan kosong, lalu buka tanda kurung dan tutup tanda kurung cocok. Kalau tidak, tidak.
Ada beberapa jawaban di sini, yang membuktikan, itu mungkin.
Jiří Herník
1
@Marco Jawaban ini berbicara tentang ekspresi reguler dalam perspektif teoretis. Banyak mesin regex sekarang menjadi hari tidak hanya mengandalkan model teoritis ini dan menggunakan beberapa memori tambahan untuk melakukan pekerjaan!
musibs
@ JiříHerník: itu bukan ekspresi reguler dalam arti yang ketat: tidak didefinisikan sebagai ekspresi reguler oleh Kleene . Beberapa mesin ekspresi reguler memang telah menerapkan beberapa kemampuan ekstra, menjadikannya parse lebih dari sekadar bahasa biasa .
Willem Van Onsem
12
Sebenarnya mungkin untuk melakukannya menggunakan .NET regular expressions, tetapi tidak sepele, jadi baca dengan cermat.
Anda dapat membaca artikel yang bagus di sini . Anda juga mungkin perlu membaca di .NET regular expressions. Anda dapat mulai membaca di sini .
Kurung sudut <>digunakan karena mereka tidak perlu keluar.
"""
Here is a simple python program showing how to use regular
expressions to write a paren-matching recursive parser.
This parser recognises items enclosed by parens, brackets,
braces and <> symbols, but is adaptable to any set of
open/close patterns. This is where the re package greatly
assists in parsing.
"""import re
# The pattern below recognises a sequence consisting of:# 1. Any characters not in the set of open/close strings.# 2. One of the open/close strings.# 3. The remainder of the string.# # There is no reason the opening pattern can't be the# same as the closing pattern, so quoted strings can# be included. However quotes are not ignored inside# quotes. More logic is needed for that....
pat = re.compile("""
( .*? )
( \( | \) | \[ | \] | \{ | \} | \< | \> |
\' | \" | BEGIN | END | $ )
( .* )
""", re.X)# The keys to the dictionary below are the opening strings,# and the values are the corresponding closing strings.# For example "(" is an opening string and ")" is its# closing string.
matching ={"(":")","[":"]","{":"}","<":">",'"':'"',"'":"'","BEGIN":"END"}# The procedure below matches string s and returns a# recursive list matching the nesting of the open/close# patterns in s.def matchnested(s, term=""):
lst =[]whileTrue:
m = pat.match(s)if m.group(1)!="":
lst.append(m.group(1))if m.group(2)== term:return lst, m.group(3)if m.group(2)in matching:
item, s = matchnested(m.group(3), matching[m.group(2)])
lst.append(m.group(2))
lst.append(item)
lst.append(matching[m.group(2)])else:raiseValueError("After <<%s %s>> expected %s not %s"%(lst, s, term, m.group(2)))# Unit test.if __name__ =="__main__":for s in("simple string",""" "double quote" """,""" 'single quote' ""","one'two'three'four'five'six'seven","one(two(three(four)five)six)seven","one(two(three)four)five(six(seven)eight)nine","one(two)three[four]five{six}seven<eight>nine","one(two[three{four<five>six}seven]eight)nine","oneBEGINtwo(threeBEGINfourENDfive)sixENDseven","ERROR testing ((( mismatched ))] parens"):print"\ninput", s
try:
lst, s = matchnested(s)print"output", lst
exceptValueErroras e:print str(e)print"done"
Sementara begitu banyak jawaban menyebutkan hal ini dalam beberapa bentuk dengan mengatakan bahwa regex tidak mendukung pencocokan rekursif dan sebagainya, alasan utama untuk ini terletak pada akar Teori Komputasi.
Bahasa formulir {a^nb^n | n>=0} is not regular. Regex hanya dapat mencocokkan hal-hal yang merupakan bagian dari rangkaian bahasa reguler.
Saya tidak menggunakan regex karena sulit untuk berurusan dengan kode bersarang. Jadi cuplikan ini harus memungkinkan Anda untuk mengambil bagian kode dengan tanda kurung seimbang:
def extract_code(data):""" returns an array of code snippets from a string (data)"""
start_pos =None
end_pos =None
count_open =0
count_close =0
code_snippets =[]for i,v in enumerate(data):if v =='{':
count_open+=1ifnot start_pos:
start_pos= i
if v=='}':
count_close +=1if count_open == count_close andnot end_pos:
end_pos = i+1if start_pos and end_pos:
code_snippets.append((start_pos,end_pos))
start_pos =None
end_pos =Nonereturn code_snippets
Saya menggunakan ini untuk mengekstrak cuplikan kode dari file teks.
/**
* get param content of function string.
* only params string should be provided without parentheses
* WORK even if some/all params are not set
* @return [param1, param2, param3]
*/
exports.getParamsSAFE =(str, nbParams =3)=>{const nextParamReg =/^\s*((?:(?:['"([{](?:[^'"()[\]{}]*?|['"([{](?:[^'"()[\]{}]*?|['"([{][^'"()[\]{}]*?['")}\]])*?['")}\]])*?['")}\]])|[^,])*?)\s*(?:,|$)/;constparams=[];while(str.length){// this is to avoid a BIG performance issue in javascript regexp engine
str = str.replace(nextParamReg,(full, p1)=>{params.push(p1);return'';});}returnparams;};
Ini tidak sepenuhnya menjawab pertanyaan OP tapi saya pikir mungkin berguna untuk beberapa yang datang ke sini untuk mencari struktur regexp bersarang.
Jawaban:
Ekspresi reguler adalah alat yang salah untuk pekerjaan itu karena Anda berurusan dengan struktur bersarang, yaitu rekursi.
Tetapi ada algoritma sederhana untuk melakukan ini, yang saya jelaskan dalam jawaban untuk pertanyaan sebelumnya .
sumber
Saya ingin menambahkan jawaban ini untuk referensi cepat. Jangan ragu untuk memperbarui.
.NET Regex menggunakan grup penyeimbang .
Di mana
c
digunakan sebagai penghitung kedalaman.Demo di Regexstorm.com
PCRE menggunakan pola rekursif .
Demo di regex101 ; Atau tanpa pergantian:
Demo di regex101 ; Atau belum dibuka untuk kinerja:
Demo di regex101 ; Pola disisipkan di
(?R)
mana mewakili(?0)
.Perl, PHP, Notepad ++, R : perl = BENAR , Paket Python : Regex dengan
(?V1)
untuk perilaku Perl.Ruby menggunakan panggilan subexpression .
Dengan Ruby 2.0
\g<0>
dapat digunakan untuk memanggil pola penuh.Demo di Rubular ; Ruby 1.9 hanya mendukung pengambilan rekursi grup :
Demo di Rubular ( pengelompokan atom sejak Ruby 1.9.3)
JavaScript API :: XRegExp.matchRecursive
JS, Java, dan rasa regex lainnya tanpa rekursi hingga 2 level bersarang:
Demo di regex101 . Sarang yang lebih dalam perlu ditambahkan ke pola.
Untuk gagal lebih cepat pada tanda kurung tidak seimbang, jatuhkan
+
quantifier.Java : Gagasan menarik menggunakan referensi ke depan oleh @jaytea .
Referensi - Apa arti dari regex ini?
sumber
(?>[^)(]+|(?R))*+
sama dengan menulis(?:[^)(]+|(?R))*+
. Hal yang sama untuk pola selanjutnya. Tentang versi yang belum dibuka, Anda dapat meletakkan quantifier posesif di sini:[^)(]*+
untuk mencegah kemunduran (jika tidak ada braket penutup).(...(..)..(..)..(..)..(..)..)
) dalam string subjek), Anda dapat menggunakan grup non-tangkapan sederhana dan melampirkan semuanya dalam grup atom:(?>(?:[^)(]+|\g<1>)*)
( ini berperilaku persis seperti quantifier posesif). Di Ruby 2.x, kuantifier posesif tersedia.Anda dapat menggunakan rekursi regex :
sumber
Unrecognized grouping construct
.[^\(]*
cocok dengan semua yang bukan braket pembuka di awal string,(\(.*\))
menangkap substring yang diperlukan yang terlampir dalam tanda kurung, dan[^\)]*
cocok dengan semua yang bukan braket penutup di akhir string. Perhatikan bahwa ungkapan ini tidak berusaha mencocokkan tanda kurung; pengurai sederhana (lihat jawaban dehmann ) akan lebih cocok untuk itu.sumber
Jika Anda ingin memilih teks di antara dua tanda kurung yang cocok , Anda kurang beruntung dengan ekspresi reguler. Ini tidak mungkin (*) .
Regex ini hanya mengembalikan teks antara pembukaan pertama dan kurung tutup terakhir di string Anda.
(*) Kecuali jika mesin regex Anda memiliki fitur seperti kelompok penyeimbang atau rekursi . Jumlah mesin yang mendukung fitur-fitur tersebut perlahan-lahan bertambah, tetapi mereka masih belum tersedia secara umum.
sumber
Jawaban ini menjelaskan batasan teoretis mengapa ekspresi reguler bukan alat yang tepat untuk tugas ini.
Ekspresi reguler tidak dapat melakukan ini.
Ekspresi reguler didasarkan pada model komputasi yang dikenal sebagai
Finite State Automata (FSA)
. Seperti namanya, aFSA
hanya dapat mengingat keadaan saat ini, ia tidak memiliki informasi tentang keadaan sebelumnya.Dalam diagram di atas, S1 dan S2 adalah dua negara di mana S1 adalah langkah awal dan akhir. Jadi jika kita mencoba dengan string
0110
, transisi berjalan sebagai berikut:Dalam langkah di atas, ketika kita berada di kedua
S2
yaitu setelah parsing01
dari0110
, FSA tidak memiliki informasi tentang sebelumnya0
di01
karena hanya bisa mengingat keadaan saat ini dan simbol input berikutnya.Dalam masalah di atas, kita perlu mengetahui no dari tanda kurung buka; ini berarti harus disimpan di suatu tempat. Tetapi karena
FSAs
tidak bisa melakukan itu, ekspresi reguler tidak dapat ditulis.Namun, suatu algoritma dapat ditulis untuk melakukan tugas ini. Algoritma umumnya jatuh di bawah
Pushdown Automata (PDA)
.PDA
adalah satu tingkat di atasFSA
. PDA memiliki tumpukan tambahan untuk menyimpan beberapa informasi tambahan. PDA dapat digunakan untuk memecahkan masalah di atas, karena kita dapat 'push
' membuka tanda kurung di tumpukan dan 'pop
' mereka begitu kita menemukan tanda kurung penutup. Jika pada akhirnya, tumpukan kosong, lalu buka tanda kurung dan tutup tanda kurung cocok. Kalau tidak, tidak.sumber
Sebenarnya mungkin untuk melakukannya menggunakan .NET regular expressions, tetapi tidak sepele, jadi baca dengan cermat.
Anda dapat membaca artikel yang bagus di sini . Anda juga mungkin perlu membaca di .NET regular expressions. Anda dapat mulai membaca di sini .
Kurung sudut
<>
digunakan karena mereka tidak perlu keluar.Ekspresi reguler terlihat seperti ini:
sumber
Ini adalah regex definitif:
Contoh:
perhatikan bahwa
'(pip'
dikelola dengan benar sebagai string. (dicoba di regulator: http://sourceforge.net/projects/regulator/ )sumber
Saya telah menulis perpustakaan JavaScript kecil yang disebut seimbang untuk membantu tugas ini. Anda dapat melakukannya dengan melakukan
Anda bahkan dapat melakukan penggantian:
Berikut ini contoh JSFiddle yang lebih kompleks dan interaktif .
sumber
Menambahkan ke jawaban gelembung berbandul , ada rasa regex lain di mana konstruksi rekursif didukung.
Lua
Gunakan
%b()
(%b{}
/%b[]
untuk kurung kurawal / kurung kotak):for s in string.gmatch("Extract (a(b)c) and ((d)f(g))", "%b()") do print(s) end
(lihat demo )Perl6 :
Kecocokan beberapa tanda kurung yang tidak tumpang tindih:
Tumpang tindih dengan beberapa tanda kurung yang seimbang:
Lihat demo .
re
Solusi non-regex PythonLihat jawaban poke untuk Cara mendapatkan ekspresi di antara tanda kurung yang seimbang .
Java solusi non-regex yang dapat disesuaikan
Berikut adalah solusi yang dapat disesuaikan yang memungkinkan pembatas literal karakter tunggal di Jawa:
Penggunaan sampel:
sumber
Ekspresi reguler menggunakan Ruby (versi 1.9.3 atau lebih tinggi):
Demo di rubular
sumber
Anda membutuhkan tanda kurung pertama dan terakhir. Gunakan sesuatu seperti ini:
str.indexOf ('('); - itu akan memberi Anda kejadian pertama
str.lastIndexOf (')'); - terakhir
Jadi, Anda perlu string antara,
sumber
sumber
Jawabannya tergantung pada apakah Anda harus mencocokkan set kurung yang cocok, atau hanya yang terbuka pertama ke penutup terakhir dalam teks input.
Jika Anda harus mencocokkan tanda kurung bersarang, maka Anda memerlukan sesuatu yang lebih dari ekspresi reguler. - lihat @dehmann
Jika hanya terbuka pertama untuk terakhir tutup lihat @ Zach
Putuskan apa yang Anda inginkan terjadi:
Anda perlu memutuskan kode apa yang perlu Anda cocokkan dalam kasus ini.
sumber
karena js regex tidak mendukung kecocokan rekursif, saya tidak dapat membuat pencocokan tanda kurung yang seimbang berfungsi.
jadi ini adalah javascript sederhana untuk versi loop yang membuat string "method (arg)" menjadi array
hasilnya seperti
sumber
Sementara begitu banyak jawaban menyebutkan hal ini dalam beberapa bentuk dengan mengatakan bahwa regex tidak mendukung pencocokan rekursif dan sebagainya, alasan utama untuk ini terletak pada akar Teori Komputasi.
Bahasa formulir
{a^nb^n | n>=0} is not regular
. Regex hanya dapat mencocokkan hal-hal yang merupakan bagian dari rangkaian bahasa reguler.Baca lebih lanjut @ sini
sumber
Saya tidak menggunakan regex karena sulit untuk berurusan dengan kode bersarang. Jadi cuplikan ini harus memungkinkan Anda untuk mengambil bagian kode dengan tanda kurung seimbang:
Saya menggunakan ini untuk mengekstrak cuplikan kode dari file teks.
sumber
Saya juga terjebak dalam situasi ini di mana pola bersarang datang.
Ekspresi Reguler adalah hal yang tepat untuk menyelesaikan masalah di atas. Gunakan pola di bawah ini
sumber
Yang ini juga berhasil
sumber
Ini mungkin berguna untuk beberapa:
Parsing parmeter dari string fungsi (dengan struktur bersarang) dalam javascript
Struktur pertandingan seperti:
Di sini Anda dapat melihat regexp yang dihasilkan beraksi
Ini tidak sepenuhnya menjawab pertanyaan OP tapi saya pikir mungkin berguna untuk beberapa yang datang ke sini untuk mencari struktur regexp bersarang.
sumber