RegEx mencocokkan tag terbuka kecuali tag XHTML mandiri


1474

Saya harus mencocokkan semua tag pembuka ini:

<p>
<a href="foo">

Tapi tidak ini:

<br />
<hr class="foo" />

Saya datang dengan ini dan ingin memastikan saya sudah benar. Saya hanya menangkap a-z.

<([a-z]+) *[^/]*?>

Saya percaya itu mengatakan:

  • Temukan yang kurang dari itu
  • Temukan (dan tangkap) az satu kali atau lebih, lalu
  • Temukan nol atau lebih banyak ruang
  • Temukan karakter nol atau lebih banyak kali, serakah, kecuali /, lalu
  • Temukan lebih besar dari

Apakah saya benar? Dan yang lebih penting, bagaimana menurut Anda?

Jawaban:


4417

Anda tidak dapat menguraikan [X] HTML dengan regex. Karena HTML tidak dapat diuraikan oleh regex. Regex bukan alat yang dapat digunakan untuk mem-parsing HTML dengan benar. Seperti yang telah saya jawab dalam pertanyaan HTML-dan-regex di sini berkali-kali sebelumnya, penggunaan regex tidak akan memungkinkan Anda untuk mengkonsumsi HTML. Ekspresi reguler adalah alat yang tidak cukup canggih untuk memahami konstruksi yang digunakan oleh HTML. HTML bukan bahasa biasa dan karenanya tidak dapat diuraikan dengan ekspresi reguler. Kueri regex tidak dilengkapi untuk memecah HTML menjadi bagian-bagian yang bermakna. berkali-kali tetapi tidak sampai ke saya. Bahkan peningkatan ekspresi reguler tidak teratur seperti yang digunakan oleh Perl tidak sesuai dengan tugas parsing HTML. Anda tidak akan pernah membuat saya retak. HTML adalah bahasa dengan kompleksitas yang cukup sehingga tidak dapat diuraikan dengan ekspresi reguler. Bahkan Jon Skeet tidak dapat mem-parsing HTML menggunakan ekspresi reguler. Setiap kali Anda mencoba mem-parsing HTML dengan ekspresi reguler, anak yang tidak suci menangis darah perawan, dan peretas Rusia membuka webapp Anda. Parsing HTML dengan regex memanggil jiwa yang tercemar ke dalam dunia yang hidup. HTML dan regex berjalan bersama seperti cinta, pernikahan, dan pembunuhan bayi ritual. <center> tidak dapat menahannya sudah terlambat. Kekuatan regex dan HTML bersama dalam ruang konseptual yang sama akan menghancurkan pikiran Anda seperti dempul yang berair. Jika Anda mem-parsing HTML dengan regex Anda menyerah pada Mereka dan cara menghujat mereka yang menghukum kita semua untuk kerja keras yang tidak manusiawi bagi Seseorang yang namanya tidak dapat dinyatakan dalam Basic Multilingual Plane, dia datang. HTML-plus-regexp akan mencairkan kebutuhan orang yang hidup sementara Anda mengamati, jiwa Anda melayang dalam serangan horor.sudah terlambat, sudah terlambat, kita tidak bisa diselamatkan . Trangession dari seorang chi ensuresld memastikan regex akan mengkonsumsi semua jaringan yang hidup (kecuali untuk HTML yang tidak bisa, seperti yang dinubuatkan sebelumnya) Tuan yang terhormat membantu kita bagaimana seseorang bisa selamat dari momok ini menggunakan regex untuk mengurai HTML telah ditakdirkan umat manusia untuk selamanya takut penyiksaan dan keamanan lubang menggunakan Rege x sebagai alat untuk proses HTML menetapkan brea ch antara dunia ini dan alam ketakutan entitas korup (seperti entitas SGML, namun lebih korup) sebuah glimp hanya se dari dunia reg mantan parser untuk HTML akan ins tantly transportasi ap kesadaran rogrammer i nto aw ORL d tak henti-hentinya berteriak, dia datang, Yang berbisa sl wil ithy regex-infeksi l melahap Anda HT ML parser, aplikasi dan eksistensi untuk semua waktu seperti Visual Basic hanya lebih buruk dia datang dia com es tidak fi GHT h e datang, HI s Unholy Radiance de stro҉ying semua pencerahan, HTML tag bocor fr̶ǫm ur yo mata seperti liq uid p ain, lagu reguler exp ulang ssion parsing akan exti nguish suara mor pria tal dari sp yang di sini saya bisa melihatnya dapat Anda lihat Hal itu t indah dia f inal snuffing o f kebohongan s of Man semua hilang A LL I SLOST th e Pony ia datang s ia com es ia bersama saya s t ia ich atau permeat es al l MY FAC E MY WAJAH ᵒh dewa n o NO noo O ON Θ berhenti t dia seorang * ̶͑̾̾ GL ES ͎a̧͈͖r̽̾̈́͒͑en ot rè̑ͧ̌aͨl̘̝̙̃ͤ͂̾̆ ZA̡͊͠͝LGΌ ISͮ̂҉̯͈͕̹̘̱ T O͇̹̺ͅƝ̴ȳ̳ TH̘ Ë͖́̉ ͠P̯͍̭O̚ N̐Y̡ H̸̡̪̯ͨ͊̽̅̾̎Ȩ̬̩̾͛ͪ̈́̀́͘ ̶̧̨̱̹̭̯ͧ̾ͬC̷̙̲̝͖ͭ̏ͥͮ͟Oͮ͏̮̪̝͍M̲̖͊̒ͪͩͬ̚̚͜Ȇ̴̟̟͙̞ͩ͌͝ S̨̥̫͎̭ͯ̿̔̀ͅ


Sudahkah Anda mencoba menggunakan parser XML?


Catatan Moderator

Posting ini dikunci untuk mencegah pengeditan yang tidak pantas untuk kontennya. Posnya terlihat persis seperti yang seharusnya terlihat - tidak ada masalah dengan kontennya. Tolong jangan panji itu untuk perhatian kami.


179
Kobi: Saya pikir sudah waktunya bagi saya untuk berhenti dari posisi Asisten Jangan Mengurai HTML Dengan Regex Officer. Tidak peduli berapa kali kita mengatakannya, mereka tidak akan berhenti datang setiap hari ... bahkan setiap jam. Ini adalah tujuan yang hilang, yang bisa diperjuangkan orang lain sedikit. Jadi, lanjutkan, parsing HTML dengan regex, jika Anda harus. Itu hanya kode rusak, bukan hidup dan mati.
bobince

27
Apakah mungkin menggunakan RegEx untuk menguraikan jawaban ini?
Chris Porter

2
Jika Anda tidak dapat melihat posting ini, berikut adalah screencapture dari semuanya dengan kemuliaan: imgur.com/gOPS2.png
Andrew Keeton

3251

Meskipun HTML sewenang-wenang dengan hanya sebuah regex tidak mungkin, kadang-kadang pantas digunakan untuk mem-parsing set HTML yang dikenal terbatas .

Jika Anda memiliki satu set kecil halaman HTML yang Anda inginkan untuk mengikis data dan kemudian memasukkannya ke dalam basis data, regex mungkin bekerja dengan baik. Sebagai contoh, saya baru-baru ini ingin mendapatkan nama, pesta, dan distrik dari perwakilan federal Australia, yang saya dapatkan dari situs web Parlemen. Ini adalah pekerjaan terbatas, satu kali.

Regex bekerja dengan baik untuk saya, dan sangat cepat untuk diatur.


131
Juga, menggores data yang diformat dengan cukup teratur dari dokumen besar akan menjadi WAY lebih cepat dengan penggunaan pindai & regex yang bijaksana daripada parser umum. Dan jika Anda merasa nyaman dengan pengkodean regex, cara kode lebih cepat daripada mengkodekan xpaths. Dan hampir pasti kurang rapuh terhadap perubahan dalam apa yang Anda gores. Jadi bleh.
Michael Johnston

255
@MichaelJohnston "Kurang rapuh"? Hampir pasti tidak. Regex peduli tentang detail pemformatan teks daripada pengurai XML yang bisa diabaikan dengan diam-diam. Beralih antara &foo;penyandian dan CDATAbagian? Menggunakan minifier HTML untuk menghapus semua spasi putih di dokumen Anda yang tidak dirender oleh browser? Pengurai XML tidak akan peduli, dan pernyataan XPath yang ditulis juga tidak akan. "Pengurai" berbasis regex, di sisi lain ...
Charles Duffy

41
@CharlesDuffy untuk pekerjaan satu kali tidak apa-apa, dan untuk spasi kita gunakan \ s +
quantum

68
@xiaomao memang, jika harus mengetahui semua kesulitan dan penyelesaian untuk mendapatkan solusi 80% yang gagal sepanjang waktu "bekerja untuk Anda", saya tidak bisa menghentikan Anda. Sementara itu, saya di sisi saya pagar menggunakan parser yang bekerja pada 100% dari XML yang valid secara sintaksis.
Charles Duffy

374
Saya pernah harus menarik beberapa data ~ 10k halaman, semua dengan template HTML yang sama. Mereka dipenuhi dengan kesalahan HTML yang menyebabkan parser tersedak, dan semua gaya mereka sejajar atau dengan <font>dll: tidak ada kelas atau ID untuk membantu menavigasi DOM. Setelah berjuang sepanjang hari dengan pendekatan "benar", saya akhirnya beralih ke solusi regex dan membuatnya bekerja dalam satu jam.
Paul A Jungwirth

2039

Saya pikir kekurangannya di sini adalah bahwa HTML adalah tata bahasa Chomsky Type 2 (tata bahasa bebas konteks) dan RegEx adalah tata bahasa Chomsky Tipe 3 (tata bahasa biasa) . Karena tata bahasa Tipe 2 secara fundamental lebih kompleks daripada tata bahasa Tipe 3 (lihat hierarki Chomsky ), secara matematis tidak mungkin untuk mem-parsing XML dengan RegEx.

Tetapi banyak yang akan mencoba, beberapa bahkan akan mengklaim sukses - tetapi sampai yang lain menemukan kesalahan dan benar-benar mengacaukan Anda.


226
OP meminta untuk menguraikan subset yang sangat terbatas dari XHTML: tag awal. Apa yang membuat (X) HTML CFG adalah potensinya untuk memiliki elemen antara tag awal dan akhir dari elemen lain (seperti dalam aturan tata bahasa A -> s A e). (X) HTML tidak memiliki properti ini dalam tag awal: tag awal tidak dapat berisi tag awal lainnya. Subset yang ingin diurai OP bukan CFG.
LarsH

101
Dalam teori CS, bahasa reguler adalah bagian ketat dari bahasa bebas konteks, tetapi implementasi ekspresi reguler dalam bahasa pemrograman umum lebih kuat. Sebagaimana dijelaskan oleh noulakaz.net/weblog/2007/03/18/… , yang disebut "ekspresi reguler" dapat memeriksa bilangan prima secara unary, yang tentunya merupakan sesuatu yang tidak dapat dicapai oleh ungkapan reguler dari teori CS.
Adam Mihalcin

11
@eyelidlessness: "hanya jika" yang sama berlaku untuk semua CFG, bukan? Yaitu jika input (X) HTML tidak terbentuk dengan baik, bahkan parser XML tidak akan bekerja dengan baik. Mungkin jika Anda memberikan contoh "(X) kesalahan sintaks HTML yang diterapkan di agen pengguna dunia nyata" yang Anda maksud, saya akan mengerti apa yang Anda dapatkan dengan lebih baik.
LarsH

82
@AdamMihalcin tepat sekali. Kebanyakan mesin regex yang ada lebih kuat dari tata bahasa Chomsky Type 3 (mis. Pencocokan non-serakah, backrefs). Beberapa mesin regex (seperti Perl) Turing lengkap. Memang benar bahwa bahkan itu adalah alat yang buruk untuk parsing HTML, tetapi argumen yang sering dikutip ini bukan alasan mengapa.
dubiousjim

27
Ini adalah jawaban yang paling "lengkap dan pendek" di sini. Ini mengarahkan orang untuk mempelajari dasar-dasar tata bahasa dan bahasa formal dan mudah-mudahan beberapa matematika sehingga mereka tidak akan membuang waktu untuk hal-hal yang tidak ada harapan seperti menyelesaikan tugas NP dalam waktu polinomial
mishmashru

1332

Jangan dengarkan mereka. Anda benar - benar dapat mengurai tata bahasa bebas konteks dengan regex jika Anda memecah tugas menjadi potongan-potongan kecil. Anda dapat menghasilkan pola yang benar dengan skrip yang melakukan masing-masing secara berurutan:

  1. Memecahkan Masalah Pemutusan.
  2. Susun sebuah lingkaran.
  3. Selesaikan Masalah Traveling Salesman dalam O (log n) atau kurang. Jika lebih dari itu, Anda akan kehabisan RAM dan mesin akan hang.
  4. Polanya akan cukup besar, jadi pastikan Anda memiliki algoritma yang tanpa kompres mengompresi data acak.
  5. Hampir sampai - hanya membagi semuanya dengan nol. Mudah marah.

Saya sendiri belum menyelesaikan bagian terakhir, tapi saya tahu saya sudah dekat. Itu terus melempar CthulhuRlyehWgahnaglFhtagnExceptionkarena beberapa alasan, jadi saya akan port ke VB 6 dan gunakan On Error Resume Next. Saya akan memperbarui dengan kode setelah saya menyelidiki pintu aneh ini yang baru saja dibuka di dinding. Hmm.

PS Pierre de Fermat juga menemukan cara melakukannya, tetapi margin yang ditulisnya tidak cukup besar untuk kode.


80
Divison dengan nol adalah masalah yang jauh lebih mudah daripada yang lain yang Anda sebutkan. Jika Anda menggunakan interval, alih-alih aritmatika floating point biasa (yang seharusnya semua orang tetapi tidak ada siapa-siapa), Anda dapat dengan senang hati membagi sesuatu dengan [interval yang mengandung] nol. Hasilnya hanyalah sebuah interval berisi plus dan minus tanpa batas.
rjmunro

148
Masalah margin kecil Fermat telah diselesaikan oleh margin lunak dalam perangkat lunak pengedit teks modern.
kd4ttc

50
Masalah margin kecil Fermat telah dipecahkan oleh Randall Munroe dengan mengatur ukuran font ke nol: xkcd.com/1381
heltonbiker

29
FYI: masalah Fermat telah benar-benar telah diselesaikan pada tahun 1995 , dan hanya butuh matematikawan 358 tahun untuk melakukannya.
jmiserez

10
Saya bisa mem-bypass langkah lengket-demi-nol itu dengan menggunakan ratchet Brown yang dihasilkan dari fusi dingin ... meskipun itu hanya bekerja ketika saya menghilangkan konstanta kosmologis.
Tim Lehner

1073

Penafian : gunakan pengurai jika Anda memiliki pilihan. Yang mengatakan ...

Ini adalah regex yang saya gunakan (!) Untuk mencocokkan tag HTML:

<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>

Mungkin tidak sempurna, tetapi saya menjalankan kode ini melalui banyak HTML. Perhatikan bahwa ia bahkan menangkap hal-hal aneh seperti <a name="badgenerator"">, yang muncul di web.

Saya kira untuk membuatnya tidak cocok dengan tag mandiri, Anda juga ingin menggunakan tampilan negatif Kobi :

<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+(?<!/\s*)>

atau hanya menggabungkan jika dan jika tidak.

Untuk downvoters: Ini adalah kode yang berfungsi dari produk yang sebenarnya. Saya ragu siapa pun yang membaca halaman ini akan mendapat kesan bahwa secara sosial dapat diterima untuk menggunakan regex pada HTML.

Peringatan : Saya harus mencatat bahwa regex ini masih rusak di hadapan blok CDATA, komentar, dan elemen skrip dan gaya. Kabar baiknya adalah, Anda dapat menyingkirkan mereka yang menggunakan ...


95
Saya akan pergi dengan sesuatu yang bekerja pada hal-hal yang waras daripada menangis tentang tidak menjadi sempurna secara universal :-)
prajeesh kumar

55
Apakah seseorang menggunakan CDATA di dalam HTML?
Danubian Sailor

16
jadi Anda tidak benar-benar menyelesaikan masalah parsing hanya dengan regexp tetapi sebagai bagian dari parser ini dapat bekerja. PS: produk yang berfungsi tidak berarti kode yang baik. Jangan tersinggung, tetapi ini adalah cara pemrograman industri bekerja dan mendapatkan uang mereka
mishmashru

32
Mulai regex Anda gagal pada kemungkinan, HTML yang valid sangat terpendek: <!doctype html><title><</title>. Sederhana '<!doctype html><title><</title>'.match(/<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>/g)kembali ["<!doctype html>", "<title>", "<</title>"]sementara harus ["<title>", "</title>"].

2
jika kita hanya mencoba mencocokkan & tidak mencocokkan dengan contoh yang diberikan, /<.([^r>[[^>[*)?>g bekerja :-) // javascript: '<p> <a href = "foo"> <br /> <class hr = "foo" />'.match(/<.([^r>][^>]*)?>/g)
imma

506

Ada orang-orang yang akan memberi tahu Anda bahwa Bumi itu bulat (atau mungkin bahwa Bumi itu bulat bulat jika mereka ingin menggunakan kata-kata aneh). Mereka berbohong.

Ada orang yang akan memberi tahu Anda bahwa Ekspresi Reguler seharusnya tidak bersifat rekursif. Mereka membatasi Anda. Mereka perlu menaklukkan Anda, dan mereka melakukannya dengan menjaga Anda dalam ketidaktahuan.

Anda dapat hidup dalam realitas mereka atau meminum pil merah.

Seperti Lord Marshal (dia kerabat dari kelas .NET Marshal?), Saya telah melihat Regex-Verse Underverse Stack Based dan kembali dengan kekuatan pengetahuan yang tidak dapat Anda bayangkan. Ya, saya pikir ada satu atau dua Old melindungi mereka, tetapi mereka menonton sepak bola di TV, jadi itu tidak sulit.

Saya pikir kasus XML cukup sederhana. RegEx (dalam sintaks .NET), dikempiskan dan dikodekan dalam base64 untuk membuatnya lebih mudah dipahami oleh pikiran lemah Anda, harus seperti ini:

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
+r/vD34mUADO1P4/AQAA//8=

Opsi untuk diatur adalah RegexOptions.ExplicitCapture . Grup tangkap yang Anda cari adalah ELEMENTNAME. Jika grup tangkap ERRORtidak kosong maka ada kesalahan parsing dan Regex berhenti.

Jika Anda memiliki masalah dalam mengubahnya menjadi regex yang dapat dibaca manusia, ini akan membantu:

static string FromBase64(string str)
{
    byte[] byteArray = Convert.FromBase64String(str);

    using (var msIn = new MemoryStream(byteArray))
    using (var msOut = new MemoryStream()) {
        using (var ds = new DeflateStream(msIn, CompressionMode.Decompress)) {
            ds.CopyTo(msOut);
        }

        return Encoding.UTF8.GetString(msOut.ToArray());
    }
}

Jika Anda tidak yakin, tidak, saya TIDAK bercanda (tapi mungkin saya berbohong). Ini akan bekerja. Saya telah membangun banyak unit test untuk mengujinya, dan saya bahkan telah menggunakan (bagian dari) tes kesesuaian . Ini adalah tokenizer, bukan pengurai penuh, sehingga hanya akan membagi XML menjadi token komponennya. Itu tidak akan menguraikan / mengintegrasikan DTD.

Oh ... jika Anda ingin kode sumber regex, dengan beberapa metode tambahan:

regex untuk menandai xml atau regex polos penuh


68
Ya Tuhan, ini sangat besar. Pertanyaan terbesar saya adalah mengapa? Anda sadar bahwa semua bahasa modern memiliki parser XML, bukan? Anda dapat melakukan semua itu dalam 3 baris dan pastikan itu akan berhasil. Selanjutnya, apakah Anda juga menyadari bahwa regex murni terbukti tidak dapat melakukan hal-hal tertentu? Kecuali Anda telah membuat parser kode regex / imperatif hibrid, tetapi tidak seperti yang Anda miliki. Bisakah Anda mengompres data acak juga?
Justin Morgan

113
@ Justin saya tidak butuh alasan. Itu bisa dilakukan (dan itu tidak ilegal / tidak bermoral), jadi saya telah melakukannya. Tidak ada batasan pikiran kecuali yang kami akui (Napoleon Hill) ... Bahasa modern dapat menguraikan XML? Betulkah? Dan saya pikir itu ilegal! :-)
xanatos

76
Pak, saya yakin. Saya akan menggunakan kode ini sebagai bagian dari kernel untuk mesin gerak abadi saya - dapatkah Anda percaya bahwa orang-orang bodoh di kantor paten terus menolak aplikasi saya? Baiklah, saya akan menunjukkannya. Saya akan menunjukkan semuanya!
Justin Morgan

31
@ Justin Jadi Parser Xml menurut definisi bebas bug, sedangkan Regex tidak? Karena jika Xml Parser tidak bebas bug menurut definisi, mungkin ada xml yang membuatnya macet dan kita kembali ke langkah 0. Katakanlah ini: baik Xml Parser dan Regex ini mencoba untuk dapat menguraikan semua "legal "XML. Mereka BISA menguraikan beberapa XML "ilegal". Bug bisa menabrak keduanya. C # XmlReader pasti lebih teruji daripada Regex ini.
xanatos

31
Tidak, tidak ada yang bebas bug: 1) Semua program mengandung setidaknya satu bug. 2) Semua program mengandung setidaknya satu baris kode sumber yang tidak perlu. 3) Dengan # 1 dan # 2 dan menggunakan induksi logis, ini masalah sederhana untuk membuktikan bahwa program apa pun dapat direduksi menjadi satu baris kode dengan bug. (dari Learning Perl)
Scott Weaver

299

Dalam shell, Anda dapat menguraikan HTML menggunakan sed :

  1. Turing
  2. Tulis parser HTML (pekerjaan rumah)
  3. ???
  4. Keuntungan!

Terkait (mengapa Anda tidak harus menggunakan pencocokan regex):


3
Saya khawatir Anda tidak mendapat lelucon, @kenorb. Tolong, baca pertanyaan dan jawaban yang diterima sekali lagi. Ini bukan tentang alat parsing HTML secara umum, atau tentang alat parsing HTML parsing, ini tentang parsing HTML melalui regex.
Palec

1
Tidak, @Abdul. Ini sepenuhnya, dapat dibuktikan (dalam pengertian matematika) tidak mungkin.
Palec

3
Ya, jawaban itu merangkumnya dengan baik, @Abdul. Perhatikan bahwa, bagaimanapun, implementasi regex tidak benar-benar ekspresi reguler dalam arti matematika - mereka memiliki konstruksi yang membuat mereka lebih kuat, sering Turing-lengkap (setara dengan tata bahasa Tipe 0). Argumen ini tidak sesuai dengan fakta ini, tetapi masih agak valid dalam arti bahwa regex tidak pernah dimaksudkan untuk mampu melakukan pekerjaan seperti itu.
Palec

2
Dan omong-omong, lelucon yang saya maksudkan adalah isi dari jawaban ini sebelum pengeditan (radikal) kenorb, khususnya revisi 4, @Abdul.
Palec

3
Yang lucu adalah bahwa OP tidak pernah diminta untuk mem-parsing html menggunakan regex. Dia diminta untuk mencocokkan teks (yang merupakan HTML) menggunakan regex. Itu sangat masuk akal.
Paralife

274

Saya setuju bahwa alat yang tepat untuk mem-parsing XML dan terutama HTML adalah parser dan bukan mesin ekspresi reguler. Namun, seperti yang telah ditunjukkan orang lain, terkadang menggunakan regex lebih cepat, lebih mudah, dan menyelesaikan pekerjaan jika Anda tahu format data.

Microsoft sebenarnya memiliki bagian Praktik Terbaik untuk Ekspresi Reguler di .NET Framework dan secara khusus berbicara tentang Mempertimbangkan Sumber Input .

Ekspresi Reguler memang memiliki batasan, tetapi sudahkah Anda mempertimbangkan yang berikut?

Kerangka .NET adalah unik ketika datang ke ekspresi reguler karena mendukung Balancing Group Definition .

Untuk alasan ini, saya yakin Anda BISA parsing XML menggunakan ekspresi reguler. Namun perlu dicatat, itu harus XML yang valid ( browser sangat memaafkan HTML dan memungkinkan sintaks XML yang buruk di dalam HTML ). Ini dimungkinkan karena "Balancing Group Definition" akan memungkinkan mesin ekspresi reguler untuk bertindak sebagai PDA.

Kutipan dari artikel 1 yang dikutip di atas:

.NET Regular Expression Engine

Seperti yang dijelaskan di atas, konstruksi seimbang yang tepat tidak dapat dijelaskan dengan ekspresi reguler. Namun, mesin ekspresi reguler .NET menyediakan beberapa konstruksi yang memungkinkan konstruksi seimbang untuk dikenali.

  • (?<group>) - Mendorong hasil yang ditangkap pada stack penangkapan dengan grup nama.
  • (?<-group>) - Munculkan tangkapan paling atas dengan grup nama dari tumpukan penangkapan.
  • (?(group)yes|no) - cocok dengan bagian ya jika ada grup dengan grup nama jika tidak cocok dengan bagian.

Konstruksi ini memungkinkan ekspresi reguler .NET untuk meniru PDA terbatas dengan dasarnya memungkinkan versi sederhana dari operasi stack: push, pop dan kosong. Operasi sederhana cukup banyak setara dengan kenaikan, penurunan dan dibandingkan dengan masing-masing nol. Ini memungkinkan .NET regular expression engine untuk mengenali subset dari bahasa bebas konteks, khususnya yang hanya memerlukan penghitung sederhana. Hal ini pada gilirannya memungkinkan untuk ekspresi reguler .NET non-tradisional untuk mengenali konstruksi seimbang yang tepat.

Pertimbangkan ungkapan reguler berikut:

(?=<ul\s+id="matchMe"\s+type="square"\s*>)
(?>
   <!-- .*? -->                  |
   <[^>]*/>                      |
   (?<opentag><(?!/)[^>]*[^/]>)  |
   (?<-opentag></[^>]*[^/]>)     |
   [^<>]*
)*
(?(opentag)(?!))

Gunakan bendera:

  • Garis tunggal
  • IgnorePatternWhitespace (tidak perlu jika Anda menutup regex dan menghapus semua spasi putih)
  • IgnoreCase (tidak perlu)

Dijelaskan Ekspresi Reguler (sebaris)

(?=<ul\s+id="matchMe"\s+type="square"\s*>) # match start with <ul id="matchMe"...
(?>                                        # atomic group / don't backtrack (faster)
   <!-- .*? -->                 |          # match xml / html comment
   <[^>]*/>                     |          # self closing tag
   (?<opentag><(?!/)[^>]*[^/]>) |          # push opening xml tag
   (?<-opentag></[^>]*[^/]>)    |          # pop closing xml tag
   [^<>]*                                  # something between tags
)*                                         # match as many xml tags as possible
(?(opentag)(?!))                           # ensure no 'opentag' groups are on stack

Anda dapat mencobanya di Penguji Ekspresi Reguler Reguler .NET yang Lebih Baik .

Saya menggunakan sumber sampel:

<html>
<body>
<div>
   <br />
   <ul id="matchMe" type="square">
      <li>stuff...</li>
      <li>more stuff</li>
      <li>
          <div>
               <span>still more</span>
               <ul>
                    <li>Another &gt;ul&lt;, oh my!</li>
                    <li>...</li>
               </ul>
          </div>
      </li>
   </ul>
</div>
</body>
</html>

Ini menemukan kecocokan:

   <ul id="matchMe" type="square">
      <li>stuff...</li>
      <li>more stuff</li>
      <li>
          <div>
               <span>still more</span>
               <ul>
                    <li>Another &gt;ul&lt;, oh my!</li>
                    <li>...</li>
               </ul>
          </div>
      </li>
   </ul>

meskipun sebenarnya keluar seperti ini:

<ul id="matchMe" type="square">           <li>stuff...</li>           <li>more stuff</li>           <li>               <div>                    <span>still more</span>                    <ul>                         <li>Another &gt;ul&lt;, oh my!</li>                         <li>...</li>                    </ul>               </div>           </li>        </ul>

Terakhir, saya sangat menikmati artikel Jeff Atwood: Parsing Html The Cthulhu Way . Cukup lucu, mengutip jawaban atas pertanyaan ini yang saat ini memiliki lebih dari 4k suara.


18
System.Textbukan bagian dari C #. Itu bagian dari .NET.
John Saunders

8
Di baris pertama regex Anda ( (?=<ul\s*id="matchMe"\s*type="square"\s*>) # match start with <ul id="matchMe"...), di antara "<ul" dan "id" seharusnya \s+, tidak \s*, kecuali jika Anda ingin cocok dengan <ulid = ...;)
C0deH4cker

@ C0deH4cker Anda benar, seharusnya ekspresi itu \s+bukan \s*.
Sam

4
Bukannya saya benar-benar memahaminya, tapi saya pikir regex Anda gagal<img src="images/pic.jpg" />
Scheintod

3
@Scheintod Terima kasih atas komentarnya. Saya memperbarui kode. Ekspresi sebelumnya gagal untuk tag penutup sendiri yang memiliki bagian /dalam yang gagal untuk <img src="images/pic.jpg" />html Anda .
Sam

258

Saya sarankan menggunakan QueryPath untuk parsing XML dan HTML dalam PHP. Pada dasarnya ini banyak sintaks yang sama dengan jQuery, hanya saja di sisi server.


8
@ Kyle — jQuery tidak mem-parsing XML, ia menggunakan pengurai bawaan klien (jika ada). Oleh karena itu Anda tidak perlu jQuery untuk melakukannya, tetapi sesederhana dua baris JavaScript lama . Jika tidak ada parser bawaan, jQuery tidak akan membantu.
RobG

1
@RobG Sebenarnya jQuery menggunakan DOM, bukan parser bawaan.
Qix - MONICA DISALAHKAN

11
@ Qix — Anda sebaiknya memberi tahu penulis dokumentasi kemudian: " jQuery.parseXML menggunakan fungsi penguraian asli browser ... ". Sumber: jQuery.parseXML ()
RobG

6
Setelah datang ke sini dari pertanyaan meme ( meta.stackexchange.com/questions/19478/the-many-memes-of-meta/… ), saya suka bahwa salah satu jawabannya adalah 'Gunakan jQuery'
Jorn

221

Meskipun jawaban yang Anda tidak dapat menguraikan HTML dengan regex benar, mereka tidak berlaku di sini. OP hanya ingin mengurai satu tag HTML dengan regex, dan itu adalah sesuatu yang dapat dilakukan dengan ekspresi reguler.

Regex yang disarankan salah, meskipun:

<([a-z]+) *[^/]*?>

Jika Anda menambahkan sesuatu ke regex, dengan melacaknya kembali dapat dipaksa untuk mencocokkan hal-hal konyol seperti <a >>, [^/]terlalu permisif. Perhatikan juga bahwa <space>*[^/]*itu mubazir, karena [^/]*bisa juga cocok dengan spasi.

Saran saya adalah

<([a-z]+)[^>]*(?<!/)>

Di mana (?<! ... )(dalam Perl regex) tampilan negatif di belakang. Bunyinya "a <, lalu kata, lalu apa pun yang bukan>, yang terakhir mungkin bukan /, diikuti oleh>".

Perhatikan bahwa ini memungkinkan hal-hal seperti <a/ >(seperti halnya regex asli), jadi jika Anda menginginkan sesuatu yang lebih ketat, Anda perlu membuat regex untuk mencocokkan pasangan atribut yang dipisahkan oleh spasi.


29
+1 untuk mencatat bahwa pertanyaannya bukan tentang parsing HTML (X) lengkap, ini tentang mencocokkan (X) tag terbuka HTML.
LarsH

10
Hal lain yang sebagian besar jawaban tampaknya diabaikan, adalah bahwa parser HTML dapat menggunakan ekspresi reguler dalam penerapannya untuk bagian-bagian HTML, dan saya akan terkejut jika sebagian besar parser tidak melakukan ini.
Thayne

@Thayne Persis. Saat mengurai masing-masing tag, ekspresi reguler adalah alat yang tepat untuk pekerjaan itu. Sangat konyol bahwa seseorang harus menggulir setengah halaman untuk menemukan jawaban yang masuk akal. Jawaban yang diterima salah karena ia mencampuradukkan lexing dan parsing.
kasperd

2
Jawaban yang diberikan di sini akan gagal ketika nilai atribut berisi karakter '>' atau '/'.
Martin L

Ini akan bekerja secara tidak benar pada HTML yang berisi komentar atau bagian CData. Itu juga tidak akan berfungsi dengan benar jika atribut yang dikutip mengandung >karakter. Saya setuju apa yang disarankan OP dapat dilakukan dengan regex, tetapi yang disajikan di sini jauh dari sederhana.
JacquesB

183

Mencoba:

<([^\s]+)(\s[^>]*?)?(?<!/)>

Ini mirip dengan milik Anda, tetapi yang terakhir >tidak boleh setelah tebasan, dan juga menerima h1.


107
<a href="foo" title="5> 3 "> Ups </a>
Gareth

21
Itu sangat benar, dan saya memang memikirkannya, tetapi saya berasumsi bahwa >simbol tersebut dengan benar lolos ke & gt ;.
Kobi

65
>valid dalam nilai atribut. Memang, dalam serialisasi 'kanonik XML' Anda tidak boleh menggunakan &gt;. (Yang tidak sepenuhnya relevan, kecuali untuk menekankan bahwa >dalam nilai atribut sama sekali bukan hal yang tidak biasa.)
bobince

5
@ Kobi: apa arti tanda seru (yang Anda letakkan di bagian akhir) di regexp?
Marco Demaio

6
@obobince: apakah kamu yakin? Saya tidak mengerti lagi, begitu juga HTML yang valid ini:<div title="this tag is a <div></div>">hello</div>
Marco Demaio

179

Sun Tzu, ahli strategi, jenderal, dan filsuf Tiongkok kuno, mengatakan:

Dikatakan bahwa jika Anda tahu musuh Anda dan mengenal diri sendiri, Anda bisa memenangkan seratus pertempuran tanpa kehilangan satu pun. Jika Anda hanya mengenal diri sendiri, tetapi bukan lawan Anda, Anda mungkin menang atau kalah. Jika Anda tidak mengenal diri sendiri atau musuh Anda, Anda akan selalu membahayakan diri sendiri.

Dalam hal ini musuh Anda adalah HTML dan Anda adalah diri sendiri atau regex. Anda bahkan mungkin Perl dengan regex tidak teratur. Tahu HTML. Kenali dirimu.

Saya telah menyusun haiku yang menggambarkan sifat HTML.

HTML has
complexity exceeding
regular language.

Saya juga telah menyusun haiku yang menggambarkan sifat regex di Perl.

The regex you seek
is defined within the phrase
<([a-zA-Z]+)(?:[^>]*[^/]*)?>

153
<?php
$selfClosing = explode(',', 'area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed');

$html = '
<p><a href="#">foo</a></p>
<hr/>
<br/>
<div>name</div>';

$dom = new DOMDocument();
$dom->loadHTML($html);
$els = $dom->getElementsByTagName('*');
foreach ( $els as $el ) {
    $nodeName = strtolower($el->nodeName);
    if ( !in_array( $nodeName, $selfClosing ) ) {
        var_dump( $nodeName );
    }
}

Keluaran:

string(4) "html"
string(4) "body"
string(1) "p"
string(1) "a"
string(3) "div"

Pada dasarnya hanya mendefinisikan nama-nama simpul elemen yang menutup sendiri, muat seluruh string html ke perpustakaan DOM, ambil semua elemen, lewati dan saring keluar yang tidak menutup sendiri dan beroperasi di atasnya.

Saya yakin Anda sudah tahu sekarang bahwa Anda seharusnya tidak menggunakan regex untuk tujuan ini.


1
Jika Anda berurusan dengan XHTML asli maka tambahkan getElementsByTagName dengan NSdan tentukan namespace.
meder omuraliev

148

Saya tidak tahu persis kebutuhan Anda untuk ini, tetapi jika Anda juga menggunakan .NET, tidak bisakah Anda menggunakan Html Agility Pack ?

Kutipan:

Ini adalah perpustakaan kode .NET yang memungkinkan Anda untuk mengurai "keluar dari web" file HTML. Parser sangat toleran dengan HTML "dunia nyata" salah format.


137

Anda ingin yang pertama >tidak didahului oleh a/ . Lihat di sini untuk detail tentang cara melakukan itu. Ini disebut sebagai tampilan negatif di belakang.

Namun, implementasi naif itu akan berakhir dengan pencocokan <bar/></foo> dalam dokumen contoh ini

<foo><bar/></foo>

Bisakah Anda memberikan sedikit informasi lebih lanjut tentang masalah yang Anda coba selesaikan? Apakah Anda melakukan iterasi melalui tag secara terprogram?


1
Ya, tentu saja. Menentukan semua tag yang saat ini terbuka, lalu membandingkannya dengan tag yang ditutup dalam array terpisah. RegEx menyakiti otak saya.
Jeff

122

W3C menjelaskan parsing dalam bentuk regexp semu:
W3C Link

Ikuti link var untuk QName, S, dan Attributeuntuk mendapatkan gambaran yang lebih jelas.
Berdasarkan itu Anda dapat membuat regexp yang cukup bagus untuk menangani hal-hal seperti tag pengupasan.


5
Itu bukan formulir psuedo regexp, itu formulir EBNF, seperti yang ditentukan di sini: XML spec, lampiran 6
Rob G

106

Jika Anda memerlukan ini untuk PHP:

The PHP DOM fungsi tidak akan berfungsi kecuali jika diformat XML. Tidak peduli seberapa baik penggunaannya bagi umat manusia.

simplehtmldom baik, tapi saya merasa agak buggy, dan itu adalah memori yang cukup berat [Akan crash pada halaman besar.]

Saya tidak pernah menggunakan querypath , jadi tidak bisa mengomentari kegunaannya.

Satu lagi untuk dicoba adalah DOMParser saya yang sangat ringan pada sumber daya dan saya telah menggunakan dengan senang hati untuk sementara waktu. Mudah dipelajari & kuat.

Untuk Python dan Java, tautan serupa diposkan.

Untuk downvoters - saya hanya menulis kelas saya ketika parser XML terbukti tidak dapat menahan penggunaan nyata. Pemunduran agama hanya mencegah agar jawaban yang bermanfaat tidak diposting - tolong simpan segala sesuatunya dalam perspektif pertanyaan.


95

Inilah solusinya:

<?php
// here's the pattern:
$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\\4\s*)*\s*(\/>|>)/';

// a string to parse:
$string = 'Hello, try clicking <a href="#paragraph">here</a>
    <br/>and check out.<hr />
    <h2>title</h2>
    <a name ="paragraph" rel= "I\'m an anchor"></a>
    Fine, <span title=\'highlight the "punch"\'>thanks<span>.
    <div class = "clear"></div>
    <br>';

// let's get the occurrences:
preg_match_all($pattern, $string, $matches, PREG_PATTERN_ORDER);

// print the result:
print_r($matches[0]);
?>

Untuk mengujinya secara mendalam, saya memasukkan tag penutup otomatis string seperti:

  1. <jam />
  2. <br/>
  3. Situs

Saya juga memasukkan tag dengan:

  1. satu atribut
  2. lebih dari satu atribut
  3. atribut yang nilainya terikat menjadi tanda kutip tunggal atau tanda kutip ganda
  4. atribut yang berisi tanda kutip tunggal ketika pembatas adalah kutipan ganda dan sebaliknya
  5. "unfetty" atribut dengan spasi sebelum simbol "=", setelahnya dan keduanya sebelum dan sesudahnya.

Jika Anda menemukan sesuatu yang tidak berfungsi dalam pembuktian konsep di atas, saya siap menganalisis kode untuk meningkatkan keterampilan saya.

<EDIT> Saya lupa bahwa pertanyaan dari pengguna adalah untuk menghindari penguraian tag penutup diri. Dalam hal ini polanya lebih sederhana, berubah menjadi ini:

$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\\4\s*)*\s*>/';

Pengguna @ridgerunner memperhatikan bahwa pola tersebut tidak mengizinkan atribut yang tidak dikutip atau atribut tanpa nilai . Dalam hal ini penyetelan yang bagus membawa kita pola berikut:

$pattern = '/<(\w+)(\s+(\w+)(\s*\=\s*(\'|"|)(.*?)\\5\s*)?)*\s*>/';

</EDIT>

Memahami polanya

Jika seseorang tertarik mempelajari lebih lanjut tentang polanya, saya berikan beberapa baris:

  1. sub-ekspresi pertama (\ w +) cocok dengan nama tag
  2. sub-ekspresi kedua berisi pola atribut. Itu disusun oleh:
    1. satu atau lebih spasi putih + s
    2. nama atribut (\ w +)
    3. nol atau lebih spasi putih * (mungkin atau tidak, kosongkan di sini)
    4. simbol "="
    5. lagi, nol atau lebih spasi putih
    6. pembatas nilai atribut, kutipan tunggal atau ganda ('| "). Dalam pola, kutipan tunggal diloloskan karena bertepatan dengan pembatas string PHP. Sub-ekspresi ini ditangkap dengan tanda kurung sehingga dapat direferensikan lagi untuk mengurai penutupan atribut, itu sebabnya sangat penting.
    7. nilai atribut, cocok dengan hampir semua hal: (. *?); dalam sintaksis khusus ini, menggunakan kecocokan serakah (tanda tanya setelah asterisk) engine RegExp memungkinkan operator mirip-lihat-ke depan, yang cocok dengan apa pun kecuali yang mengikuti sub-ekspresi ini
    8. inilah kesenangannya: bagian \ 4 adalah operator backreference , yang mengacu pada sub-ekspresi yang didefinisikan sebelumnya dalam pola, dalam hal ini, saya merujuk pada sub-ekspresi keempat, yang merupakan pembatas atribut pertama yang ditemukan
    9. nol atau lebih spasi putih *
    10. sub-ekspresi atribut berakhir di sini, dengan spesifikasi nol atau lebih kejadian yang mungkin, diberikan oleh asterisk.
  3. Kemudian, karena sebuah tag dapat diakhiri dengan spasi putih sebelum simbol ">", nol atau lebih spasi putih dicocokkan dengan subkotak \ s *.
  4. Tag yang cocok mungkin diakhiri dengan simbol ">" sederhana, atau kemungkinan penutupan XHTML, yang menggunakan slash sebelumnya: (/> |>). Slash, tentu saja, lolos karena bertepatan dengan pembatas ekspresi reguler.

Kiat kecil: untuk menganalisis kode ini dengan lebih baik, perlu melihat kode sumber yang dihasilkan karena saya tidak memberikan karakter khusus HTML yang lolos.


12
Tidak cocok dengan tag yang valid yang memiliki atribut tanpa nilai, yaitu <option selected>. Juga tidak cocok dengan tag yang valid dengan nilai atribut yang tidak dikutip, yaitu <p id=10>.
ridgerunner

1
@ridgerunner: Terima kasih banyak atas komentar Anda. Dalam hal ini polanya harus sedikit berubah: $ pattern = '/ <(\ w +) (\ s + (\ w +) (\ s * \ = \ s * (\' | "|) | ((*?) \\ 5 \ s *)?) * \ S *> / '; Saya mengujinya dan berfungsi jika atribut atau atribut tidak dikutip tanpa nilai.
Emanuele Del Grande

Bagaimana dengan spasi sebelum nama tag: < a href="http://wtf.org" >Saya cukup yakin itu legal, tetapi Anda tidak mencocokkannya.
Floris

7
Maaf, spasi putih sebelum tagname ilegal. Selain "cukup yakin" mengapa Anda tidak memberikan beberapa bukti keberatan Anda? Ini milik saya, w3.org/TR/xml11/#sec-starttags yang mengacu pada XML 1.1, dan Anda dapat menemukan hal yang sama untuk HTML 4, 5 dan XHTML, karena validasi W3C juga akan memperingatkan jika Anda melakukan tes. Seperti banyak penyair bla-blah lainnya di sekitar sini, saya masih belum menerima argumentasi yang cerdas, selain ratusan minus pada jawaban saya, untuk menunjukkan di mana kode saya gagal sesuai dengan aturan kontrak yang ditentukan dalam pertanyaan. Saya hanya akan menyambut mereka.
Emanuele Del Grande

@ pemula tentu saja komentar Anda cerdas dan selamat datang.
Emanuele Del Grande

91

Setiap kali saya perlu mengekstrak sesuatu dengan cepat dari dokumen HTML, saya menggunakan Tidy untuk mengubahnya menjadi XML dan kemudian menggunakan XPath atau XSLT untuk mendapatkan yang saya butuhkan. Dalam kasus Anda, sesuatu seperti ini:

//p/a[@href='foo']

89

Saya menggunakan alat open source yang disebut HTMLParser sebelumnya. Ini dirancang untuk mem-parsing HTML dengan berbagai cara dan melayani tujuan dengan cukup baik. Ia dapat mem-parsing HTML sebagai treenode yang berbeda dan Anda dapat dengan mudah menggunakan API-nya untuk mendapatkan atribut dari node. Lihat dan lihat apakah ini bisa membantu Anda.


84

Saya suka mem-parsing HTML dengan ekspresi reguler. Saya tidak mencoba mengurai HTML idiot yang sengaja rusak. Kode ini adalah parser utama saya (edisi Perl):

$_ = join "",<STDIN>; tr/\n\r \t/ /s; s/</\n</g; s/>/>\n/g; s/\n ?\n/\n/g;
s/^ ?\n//s; s/ $//s; print

Ini disebut htmlsplit, memecah HTML menjadi beberapa baris, dengan satu tag atau potongan teks pada setiap baris. Baris kemudian dapat diproses lebih lanjut dengan alat teks dan skrip lain, seperti grep , sed , Perl, dll. Aku bahkan tidak bercanda :) Selamat menikmati.

Cukup sederhana untuk mengubah script Perl saya slurp-semuanya-pertama menjadi hal streaming yang bagus, jika Anda ingin memproses halaman web yang sangat besar. Tapi itu tidak terlalu penting.

Saya yakin saya akan mendapatkan suara untuk ini.

HTML Split


Terhadap harapan saya ini mendapat beberapa upvotes, jadi saya akan menyarankan beberapa ekspresi reguler yang lebih baik:

/(<.*?>|[^<]+)\s*/g    # get tags and text
/(\w+)="(.*?)"/g       # get attibutes

Mereka bagus untuk XML / XHTML.

Dengan variasi kecil, ini dapat mengatasi HTML yang berantakan ... atau mengonversi HTML -> XHTML terlebih dahulu.


Cara terbaik untuk menulis ekspresi reguler adalah dalam gaya Lex / Yacc , bukan sebagai satu-baris buram atau berkomentar monstrositas multi-line. Saya belum melakukannya di sini; yang ini nyaris tidak membutuhkannya.


35
"Saya tidak berusaha mengurai HTML idiot yang sengaja rusak." Bagaimana kode Anda mengetahui perbedaannya?
Kevin Panko

Yah tidak masalah banyak jika HTMLnya rusak atau tidak. Masalahnya masih akan membagi HTML menjadi tag dan teks. Satu-satunya hal yang dapat mengacaukannya adalah jika orang menyertakan karakter <atau> yang tidak terhapuskan dalam teks atau atribut. Dalam praktiknya, splitter HTML mungil saya berfungsi dengan baik. Saya tidak perlu ganjalan monstrositas besar penuh dengan heuristik. Solusi sederhana bukan untuk semua orang ...!
Sam Watkins

Saya menambahkan beberapa regexps sederhana untuk mengekstraksi tag, teks, dan atribut, untuk XML / XHTML.
Sam Watkins

(dapatkan atribut bug 1) /(\w+)="(.*?)"/mengasumsikan tanda kutip ganda. Ini akan kehilangan nilai dalam tanda kutip tunggal. Dalam html versi 4 dan nilai kuotasi sebelumnya diperbolehkan, jika itu kata yang sederhana.
David Andersson

(dapatkan atribut bug 2) /(\w+)="(.*?)"/dapat secara keliru mencocokkan teks yang tampak seperti atribut dalam atribut, misalnya <img title="Nope down='up' for aussies" src="..." />. Jika diterapkan secara global, itu juga akan cocok dengan hal-hal seperti itu dalam teks biasa atau dalam komentar html.
David Andersson

74

Berikut adalah parser berbasis PHP yang mem-parsing HTML menggunakan beberapa regex yang tidak bertuhan. Sebagai penulis proyek ini, saya dapat memberitahu Anda mungkin untuk mem-parsing HTML dengan regex, tetapi tidak efisien. Jika Anda memerlukan solusi sisi server (seperti yang saya lakukan untuk plugin wp-Typography WordPress ), ini berfungsi.


1
htmlawed adalah proyek PHP lain yang mem-parsing HTML untuk memfilter, mengkonversi, dll. Memiliki beberapa kode yang bagus jika Anda dapat mengetahuinya!
user594694

Tidak, Anda tidak dapat menguraikan HTML dengan regex. Tetapi untuk beberapa himpunan bagian, ini mungkin berhasil.
mirabilos

71

Ada beberapa regex yang bagus untuk mengganti HTML dengan BBCode di sini . Untuk semua yang Anda sampaikan, perhatikan bahwa dia tidak mencoba mem-parsing HTML sepenuhnya, hanya untuk membersihkannya. Ia mungkin dapat membunuh tag yang tidak dapat dimengerti "parser" sederhana.

Sebagai contoh:

$store =~ s/http:/http:\/\//gi;
$store =~ s/https:/https:\/\//gi;
$baseurl = $store;

if (!$query->param("ascii")) {
    $html =~ s/\s\s+/\n/gi;
    $html =~ s/<pre(.*?)>(.*?)<\/pre>/\[code]$2\[\/code]/sgmi;
}

$html =~ s/\n//gi;
$html =~ s/\r\r//gi;
$html =~ s/$baseurl//gi;
$html =~ s/<h[1-7](.*?)>(.*?)<\/h[1-7]>/\n\[b]$2\[\/b]\n/sgmi;
$html =~ s/<p>/\n\n/gi;
$html =~ s/<br(.*?)>/\n/gi;
$html =~ s/<textarea(.*?)>(.*?)<\/textarea>/\[code]$2\[\/code]/sgmi;
$html =~ s/<b>(.*?)<\/b>/\[b]$1\[\/b]/gi;
$html =~ s/<i>(.*?)<\/i>/\[i]$1\[\/i]/gi;
$html =~ s/<u>(.*?)<\/u>/\[u]$1\[\/u]/gi;
$html =~ s/<em>(.*?)<\/em>/\[i]$1\[\/i]/gi;
$html =~ s/<strong>(.*?)<\/strong>/\[b]$1\[\/b]/gi;
$html =~ s/<cite>(.*?)<\/cite>/\[i]$1\[\/i]/gi;
$html =~ s/<font color="(.*?)">(.*?)<\/font>/\[color=$1]$2\[\/color]/sgmi;
$html =~ s/<font color=(.*?)>(.*?)<\/font>/\[color=$1]$2\[\/color]/sgmi;
$html =~ s/<link(.*?)>//gi;
$html =~ s/<li(.*?)>(.*?)<\/li>/\[\*]$2/gi;
$html =~ s/<ul(.*?)>/\[list]/gi;
$html =~ s/<\/ul>/\[\/list]/gi;
$html =~ s/<div>/\n/gi;
$html =~ s/<\/div>/\n/gi;
$html =~ s/<td(.*?)>/ /gi;
$html =~ s/<tr(.*?)>/\n/gi;

$html =~ s/<img(.*?)src="(.*?)"(.*?)>/\[img]$baseurl\/$2\[\/img]/gi;
$html =~ s/<a(.*?)href="(.*?)"(.*?)>(.*?)<\/a>/\[url=$baseurl\/$2]$4\[\/url]/gi;
$html =~ s/\[url=$baseurl\/http:\/\/(.*?)](.*?)\[\/url]/\[url=http:\/\/$1]$2\[\/url]/gi;
$html =~ s/\[img]$baseurl\/http:\/\/(.*?)\[\/img]/\[img]http:\/\/$1\[\/img]/gi;

$html =~ s/<head>(.*?)<\/head>//sgmi;
$html =~ s/<object>(.*?)<\/object>//sgmi;
$html =~ s/<script(.*?)>(.*?)<\/script>//sgmi;
$html =~ s/<style(.*?)>(.*?)<\/style>//sgmi;
$html =~ s/<title>(.*?)<\/title>//sgmi;
$html =~ s/<!--(.*?)-->/\n/sgmi;

$html =~ s/\/\//\//gi;
$html =~ s/http:\//http:\/\//gi;
$html =~ s/https:\//https:\/\//gi;

$html =~ s/<(?:[^>'"]*|(['"]).*?\1)*>//gsi;
$html =~ s/\r\r//gi;
$html =~ s/\[img]\//\[img]/gi;
$html =~ s/\[url=\//\[url=/gi;

15
Jangan lakukan ini. Silahkan.
maletor

68

Tentang pertanyaan tentang metode RegExp untuk mem-parsing (x) HTML, jawaban untuk semua yang berbicara tentang beberapa batasan adalah: Anda belum cukup terlatih untuk mengatur kekuatan senjata yang kuat ini, karena NOBODY di sini berbicara tentang rekursi .

Rekan RegExp-agnostik memberi tahu saya tentang diskusi ini, yang bukan yang pertama di web tentang topik lama dan panas ini.

Setelah membaca beberapa posting, hal pertama yang saya lakukan adalah mencari string "? R" di utas ini. Yang kedua adalah mencari tentang "rekursi".
Tidak, sapi suci, tidak ada kecocokan yang ditemukan.
Karena tidak ada yang menyebutkan mekanisme utama parser dibangun, saya segera menyadari bahwa tidak ada yang mengerti.

Jika parser (x) HTML membutuhkan rekursi, parser RegExp tanpa rekursi tidak cukup untuk tujuannya. Ini adalah konstruksi sederhana.

The art hitam RegExp sulit untuk menguasai , jadi mungkin ada kemungkinan lebih lanjut kami ditinggalkan ketika mencoba dan menguji solusi pribadi kita untuk menangkap seluruh web di satu tangan ... Nah, saya yakin tentang hal itu :)

Inilah pola ajaibnya:

$pattern = "/<([\w]+)([^>]*?)(([\s]*\/>)|(>((([^<]*?|<\!\-\-.*?\-\->)|(?R))*)<\/\\1[\s]*>))/s";

Cobalah.
Ini ditulis sebagai string PHP, sehingga pengubah "s" membuat kelas menyertakan baris baru.
Berikut contoh catatan pada manual PHP yang saya tulis pada Januari: Referensi

(Berhati-hatilah, dalam catatan itu saya salah menggunakan pengubah "m"; harus dihapus, meskipun dibuang oleh mesin RegExp, karena tidak ada ^ atau $ anchorage digunakan).

Sekarang, kita dapat berbicara tentang batasan metode ini dari sudut pandang yang lebih luas:

  1. menurut implementasi spesifik dari mesin RegExp, rekursi mungkin memiliki batas dalam jumlah pola bersarang yang diuraikan , tetapi itu tergantung pada bahasa yang digunakan
  2. meskipun rusak (x) HTML tidak menyebabkan kesalahan yang parah, itu tidak dibersihkan .

Bagaimanapun itu hanya pola RegExp, tetapi ia mengungkapkan kemungkinan untuk mengembangkan banyak implementasi yang kuat.
Saya menulis pola ini untuk menggerakkan parser keturunan rekursif dari mesin template yang saya buat dalam kerangka kerja saya, dan kinerja sangat bagus, baik dalam waktu eksekusi atau dalam penggunaan memori (tidak ada hubungannya dengan mesin template lain yang menggunakan sintaksis yang sama).


35
Saya akan meletakkan ini di "Regex yang tidak memungkinkan lebih besar dari pada atribut" bin. Periksa apakah <input value = "is 5> 3?" />
Gareth

68
Jika Anda memasukkan sesuatu seperti itu dalam kode produksi, Anda kemungkinan akan ditembak oleh pengelola. Juri tidak akan pernah menghukumnya.
aehiilrs

30
Ekspresi reguler tidak dapat berfungsi karena menurut definisi mereka tidak rekursif. Menambahkan operator rekursif ke ekspresi reguler pada dasarnya membuat CFG hanya dengan sintaks yang lebih buruk. Mengapa tidak menggunakan sesuatu yang dirancang untuk menjadi rekursif di tempat pertama daripada menyisipkan rekursi ke dalam sesuatu yang sudah dipenuhi dengan fungsi asing?
Welbog

16
Keberatan saya bukan salah satu fungsi, ini salah satu waktu yang diinvestasikan. Masalah dengan RegEx adalah bahwa pada saat Anda memposting cutsey si kecil liner tampaknya Anda melakukan sesuatu yang lebih efisien ("Lihat satu baris kode!"). Dan tentu saja tidak ada yang menyebutkan setengah jam (atau 3) yang mereka habiskan dengan cheat-sheet mereka dan (mudah-mudahan) menguji setiap permutasi input yang mungkin. Dan setelah Anda melewati semua itu ketika pengelola pergi untuk mencari tahu atau memvalidasi kode mereka tidak bisa hanya melihatnya dan melihat bahwa itu benar. Harus membedah ekspresi dan pada dasarnya menguji lagi ...
Oorang

15
... untuk mengetahui bahwa itu baik. Dan itu akan terjadi bahkan dengan orang yang baik dengan regex. Dan jujur ​​saya curiga bahwa mayoritas orang tidak akan mengetahuinya dengan baik. Jadi Anda mengambil salah satu mimpi buruk pemeliharaan yang paling terkenal dan menggabungkannya dengan rekursi yang merupakan mimpi buruk pemeliharaan lainnya dan saya berpikir dalam hati apa yang benar-benar saya butuhkan pada proyek saya adalah seseorang yang sedikit kurang pintar. Tujuannya adalah untuk menulis kode yang dapat dipertahankan oleh programmer yang buruk tanpa merusak basis kode. Saya tahu itu harus kode ke penyebut paling umum. Tetapi mempekerjakan bakat luar biasa itu sulit, dan Anda sering ...
Oorang

62

Seperti yang telah ditunjukkan oleh banyak orang, HTML bukanlah bahasa biasa yang dapat membuatnya sangat sulit untuk diuraikan. Solusi saya untuk ini adalah mengubahnya menjadi bahasa biasa menggunakan program rapi dan kemudian menggunakan parser XML untuk mengkonsumsi hasilnya. Ada banyak opsi bagus untuk ini. Program saya ditulis menggunakan Java dengan jtidy library untuk mengubah HTML menjadi XML dan kemudian Jaxen untuk xpath menjadi hasilnya.


61
<\s*(\w+)[^/>]*>

Bagian-bagian menjelaskan:

<: karakter awal

\s*: mungkin memiliki spasi putih sebelum nama tag (jelek tapi mungkin).

(\w+): tag dapat berisi huruf dan angka (h1). Yah, \wjuga cocok dengan '_', tapi kurasa tidak ada salahnya. Jika ingin tahu gunakan ([a-zA-Z0-9] +) sebagai gantinya.

[^/>]*: apa pun kecuali >dan /sampai ditutup>

>: penutupan >

TIDAK TERKAIT

Dan untuk orang-orang yang meremehkan ekspresi reguler mengatakan mereka hanya sekuat bahasa biasa:

a n ba n ba n yang tidak teratur dan bahkan tidak bebas konteks, dapat dicocokkan dengan^(a+)b\1b\1$

Referensi Balik FTW !


@ GitchMr, itu maksudnya. Ungkapan reguler modern tidak teratur secara teknis, juga tidak ada alasan untuk itu.
alanaktion

3
@alanaktion: Ekspresi reguler "modern" (baca: dengan ekstensi Perl) tidak dapat cocok dengan O(MN)(M menjadi panjang ekspresi reguler, N menjadi panjang teks). Referensi balik adalah salah satu penyebabnya. Implementasi dalam awk tidak memiliki referensi kembali dan cocok dengan semua dalam O(MN)waktu.
Konrad Borowski

56

Jika Anda hanya berusaha menemukan tag-tag itu (tanpa ambisi untuk penguraian), cobalah ungkapan reguler ini:

/<[^/]*?>/g

Saya menulisnya dalam 30 detik, dan diuji di sini: http://gskinner.com/RegExr/

Itu cocok dengan jenis tag yang Anda sebutkan, sementara mengabaikan jenis yang Anda katakan ingin Anda abaikan.


2
Saya pikir maksud Anda \/>alih-alih \\>.
Justin Morgan

Tidak, hanya \>apa yang saya maksudkan; Saya tidak pernah bermaksud mengedit ekspresi reguler dari posting asli saya.
Lonnie Best

2
FYI, Anda tidak perlu keluar dari kurung siku. Tentu saja, tidak ada salahnya melarikan diri dari mereka, tetapi lihatlah kebingungan yang bisa Anda hindari. ;)
Alan Moore

Terkadang saya melarikan diri secara tidak perlu ketika saya tidak yakin apakah ada karakter khusus atau tidak. Saya telah mengedit jawabannya; kerjanya sama tetapi lebih ringkas.
Lonnie Best

Melihat ini sekarang, saya tidak tahu mengapa saya pikir Anda maksudkan \/, karena itu akan melakukan kebalikan dari persyaratan. Mungkin saya pikir Anda menawarkan pola filter negatif.
Justin Morgan

54

Menurut saya Anda mencoba mencocokkan tag tanpa tanda "/" pada akhirnya. Coba ini:

<([a-zA-Z][a-zA-Z0-9]*)[^>]*(?<!/)>

8
Ini tidak bekerja. Untuk input '<xa = "<b>" /> <y>' kecocokannya adalah x dan y, meskipun x dihentikan.
ceving

51

Memang benar bahwa ketika pemrograman biasanya terbaik untuk menggunakan parser dan API khusus daripada ekspresi reguler ketika berhadapan dengan HTML, terutama jika akurasi sangat penting (misalnya, jika pemrosesan Anda mungkin memiliki implikasi keamanan). Namun, saya tidak menganggap pandangan dogmatis bahwa markup gaya XML tidak boleh diproses dengan ekspresi reguler. Ada beberapa kasus ketika ekspresi reguler adalah alat yang hebat untuk pekerjaan itu, seperti ketika melakukan pengeditan satu kali dalam editor teks, memperbaiki file XML yang rusak, atau berurusan dengan format file yang terlihat seperti tetapi tidak cukup XML. Ada beberapa masalah yang harus diperhatikan, tetapi tidak dapat diatasi atau bahkan relevan.

Regex sederhana seperti <([^>"']|"[^"]*"|'[^']*')*>biasanya cukup baik, dalam kasus-kasus seperti yang baru saja saya sebutkan. Ini adalah solusi yang naif, semua hal dipertimbangkan, tetapi memang benar memungkinkan tanpa enkripsi> simbol yang tidak di- dalam nilai atribut. Jika Anda mencari, misalnya tabletag, Anda dapat menyesuaikannya sebagai </?table\b([^>"']|"[^"]*"|'[^']*')*>.

Hanya untuk memberikan gambaran seperti apa regex HTML yang lebih "canggih" akan terlihat, berikut ini melakukan pekerjaan yang cukup terhormat untuk meniru perilaku browser dunia nyata dan algoritma parsing HTML5:

</?([A-Za-z][^\s>/]*)(?:=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)|[^>])*(?:>|$)

Berikut ini cocok dengan definisi tag XML yang cukup ketat (meskipun tidak memperhitungkan set lengkap karakter Unicode yang diizinkan dalam nama XML):

<(?:([_:A-Z][-.:\w]*)(?:\s+[_:A-Z][-.:\w]*\s*=\s*(?:"[^"]*"|'[^']*'))*\s*/?|/([_:A-Z][-.:\w]*)\s*)>

Memang, ini tidak memperhitungkan konteks sekitar dan beberapa kasus tepi, tetapi bahkan hal-hal seperti itu dapat diatasi jika Anda benar-benar ingin (misalnya, dengan mencari di antara pertandingan dari regex lain).

Pada akhirnya, gunakan alat yang paling tepat untuk pekerjaan itu, bahkan dalam kasus ketika alat itu kebetulan merupakan regex.


49

Meskipun itu tidak cocok dan efektif untuk menggunakan ekspresi reguler untuk tujuan itu, kadang-kadang ekspresi reguler memberikan solusi cepat untuk masalah pencocokan sederhana dan dalam pandangan saya itu tidak terlalu mengerikan untuk menggunakan ekspresi reguler untuk pekerjaan sepele.

Ada posting blog definitif tentang pencocokan elemen HTML paling dalam yang ditulis oleh Steven Levithan.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.