Bisakah Anda memberikan beberapa contoh mengapa sulit untuk mem-parsing XML dan HTML dengan sebuah regex? [Tutup]

402

Satu kesalahan yang saya lihat orang membuat berulang - ulang adalah mencoba mem-parsing XML atau HTML dengan regex. Berikut adalah beberapa alasan mengapa parsing XML dan HTML sulit:

Orang ingin memperlakukan file sebagai urutan baris, tetapi ini valid:

<tag
attr="5"
/>

Orang-orang ingin memperlakukan tag <atau <sebagai awal tag, tetapi hal-hal seperti ini ada di alam liar:

<img src="imgtag.gif" alt="<img>" />

Orang-orang sering ingin mencocokkan tag awal dengan tag akhir, tetapi XML dan HTML memungkinkan tag mengandung diri mereka sendiri (yang tidak bisa ditangani oleh regex tradisional sama sekali):

<span id="outer"><span id="inner">foo</span></span> 

Orang sering ingin mencocokkan dengan konten dokumen (seperti masalah terkenal "temukan semua nomor telepon pada halaman tertentu"), tetapi data dapat ditandai (bahkan jika itu terlihat normal jika dilihat):

<span class="phonenum">(<span class="area code">703</span>)
<span class="prefix">348</span>-<span class="linenum">3020</span></span>

Komentar mungkin berisi tag yang diformat dengan buruk atau tidak lengkap:

<a href="foo">foo</a>
<!-- FIXME:
    <a href="
-->
<a href="bar">bar</a>

Apa saja Gotcha lain yang Anda ketahui?

Chas. Owens
sumber
14
Peramban web memahami jenis kekacauan ini jutaan kali per detik, tidak bisakah seseorang membuat kelas parser halaman web bagi kita manusia biasa?
Jon Winstanley
24
Jon, mereka punya. Di Perl ada banyak HTML :: Parser, HTML :: TreeBuilder, dll. Hampir pasti ada satu untuk bahasa Anda.
Chas. Owens
12
Jawaban terbaik adalah, stackoverflow.com/a/1732454/135078 (Beware Zalgo)
Kelly S. French
3
Ada penjelasan yang bagus mengapa [Anda tidak dapat menguraikan [X] HTML dengan regex] [1] [1]: stackoverflow.com/a/1732454/468725
Pavel P
4
Berikut adalah penjelasan yang bagus tentang bagaimana Anda dapat menguraikan HTML dengan pola , serta mengapa Anda mungkin tidak ingin melakukannya.
tchrist

Jawaban:

260

Berikut ini beberapa XML valid yang menyenangkan untuk Anda:

<!DOCTYPE x [ <!ENTITY y "a]>b"> ]>
<x>
    <a b="&y;>" />
    <![CDATA[[a>b <a>b <a]]>
    <?x <a> <!-- <b> ?> c --> d
</x>

Dan kumpulan kecil kesenangan ini adalah HTML yang valid:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd" [
    <!ENTITY % e "href='hello'">
    <!ENTITY e "<a %e;>">
]>
    <title>x</TITLE>
</head>
    <p id  =  a:b center>
    <span / hello </span>
    &amp<br left>
    <!---- >t<!---> < -->
    &e link </a>
</body>

Belum lagi semua penguraian khusus browser untuk konstruksi yang tidak valid.

Semoga berhasil mengadu domba regex melawan itu!

EDIT (Jörg W Mittag): Ini adalah bagian lain yang bagus dari HTML 4.01 yang valid dan bagus:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
  "http://www.w3.org/TR/html4/strict.dtd"> 
<HTML/
  <HEAD/
    <TITLE/>/
    <P/>
bobince
sumber
6
Yang XML? Ada beberapa konstruksi yang berbeda di sana, mana yang merepotkan? Subset internal DTD? Itu mendefinisikan & entitas baru; disebut 'y', berisi urutan ']>' yang biasanya, jika tidak dalam tanda kutip, akhiri bagian internal.
bobince
16
(Ini menunjukkan bahwa Anda harus memiliki pengetahuan yang cukup mendalam tentang beberapa fitur DTD yang lebih esoterik dan kuno dari XML untuk mem-parsing dokumen dengan benar, bahkan jika Anda bukan parser validasi DTD.)
bobince
17
Contoh HTML memanfaatkan fitur yang jarang diketahui: shorttags. Baca lebih lanjut di w3.org/QA/2007/10/shorttags.html
netvope
25
Setiap kali seseorang menulis HTML seperti yang ditunjukkan di atas, Tim Berners-Lee meneteskan air mata.
fgysin mengembalikan Monica
5
Saya suka bagaimana stabilo Sintaks Stackoverflow gagal pada kemunculan pertama "]".
GlassGhost
71

Sebenarnya

<img src="imgtag.gif" alt="<img>" />

juga bukan HTML yang valid, dan juga bukan XML yang valid.

Ini bukan XML yang valid karena '<' dan '>' bukan karakter yang valid di dalam string atribut. Mereka harus melarikan diri menggunakan entitas XML yang sesuai & lt; dan & gt;

Itu juga bukan HTML yang valid karena formulir penutupan pendek tidak diperbolehkan dalam HTML (tetapi benar dalam XML dan XHTML). Tag 'img' juga merupakan tag tertutup secara implisit sesuai dengan spesifikasi HTML 4.01. Ini berarti bahwa menutupnya secara manual sebenarnya salah, dan setara dengan menutup tag lain dua kali.

Versi yang benar dalam HTML adalah

<img src="imgtag.gif" alt="&lt;img&gt;">

dan versi yang benar dalam XHTML dan XML adalah

<img src="imgtag.gif" alt="&lt;img&gt;"/>

Contoh berikut yang Anda berikan juga tidak valid

<
tag
attr="5"
/>

Ini juga bukan HTML atau XML yang valid. Nama tag harus tepat di belakang '<', meskipun atribut dan penutupan '>' ada di mana pun mereka inginkan. Jadi XML yang valid sebenarnya

<tag
attr="5"
/>

Dan inilah satu lagi yang lebih lucu: Anda sebenarnya dapat memilih untuk menggunakan "atau 'sebagai karakter kutipan atribut Anda

<img src="image.gif" alt='This is single quoted AND valid!'>

Semua alasan lain yang diposting sudah benar, tetapi masalah terbesar dengan parsing HTML adalah bahwa orang biasanya tidak memahami semua aturan sintaks dengan benar. Fakta bahwa browser Anda mengartikan tagoup Anda sebagai HTML tidak berarti bahwa Anda telah benar-benar menulis HTML yang valid.

Sunting: Dan bahkan stackoverflow.com setuju dengan saya mengenai definisi yang valid dan tidak valid. XML / HTML Anda yang tidak valid tidak disorot, sedangkan versi saya yang diperbaiki adalah.

Pada dasarnya, XML tidak dibuat untuk diuraikan dengan regexps. Tetapi tidak ada alasan untuk melakukannya. Ada banyak, banyak parser XML untuk masing-masing dan setiap bahasa. Anda memiliki pilihan antara parser SAX, parser DOM dan parser Tarik. Semua ini dijamin jauh lebih cepat daripada parsing dengan regexp dan Anda kemudian dapat menggunakan teknologi keren seperti XPath atau XSLT pada pohon DOM yang dihasilkan.

Karenanya, jawaban saya adalah: tidak hanya mem-parsing XML dengan regexps sulit, tetapi juga merupakan ide yang buruk. Cukup gunakan salah satu dari jutaan parser XML yang ada, dan manfaatkan semua fitur canggih XML.

HTML terlalu sulit bahkan untuk mencoba parsing sendiri. Pertama, sintaksis hukum memiliki banyak seluk-beluk kecil yang mungkin tidak Anda sadari, dan kedua, HTML di alam liar hanyalah tumpukan besar yang berbau busuk (Anda mengerti maksud saya). Ada berbagai pustaka lax parser yang melakukan pekerjaan dengan baik dalam menangani HTML seperti sup tag, cukup gunakan ini.

LordOfThePigs
sumber
8
>> Anda tidak perlu melarikan diri.
Joey
8
Oke, s / valid / ada di wild / g
Chas. Owens
1
Sebenarnya, sesuai dengan spesifikasi Anda harus melarikan diri> sebagai> sama seperti Anda harus melarikan diri <sebagai <& dan & amp; dan dalam atribut "as & quot; dan 'as & apos; hanya saja banyak parser
LordOfThePigs
19
Spesifikasi tidak mengatakan '>' harus lolos - kecuali untuk kasus khusus dari urutan ']]>' dalam konten. Karena alasan ini, paling mudah untuk selalu melarikan diri '>', tetapi tidak diharuskan oleh spek.
bobince
8
>tanda benar-benar berlaku di html stackoverflow.com/questions/94528/...
jfs
56

Saya menulis seluruh entri blog tentang hal ini: Batasan Ekspresi Reguler

Inti dari masalah ini adalah bahwa HTML dan XML adalah struktur rekursif yang membutuhkan mekanisme penghitungan agar dapat diurai dengan benar. Regex yang benar tidak dapat menghitung. Anda harus memiliki tata bahasa gratis konteks untuk menghitung.

Paragraf sebelumnya hadir dengan sedikit peringatan. Implementasi regex tertentu sekarang mendukung gagasan rekursi. Namun begitu Anda mulai menambahkan rekursi ke dalam ekspresi regex Anda, Anda benar-benar merentangkan batas dan harus mempertimbangkan parser.

JaredPar
sumber
20

Satu hal yang tidak ada dalam daftar Anda adalah bahwa atribut dapat muncul dalam urutan apa pun, jadi jika regex Anda mencari tautan dengan href "foo" dan kelas "bar", mereka dapat datang dalam urutan apa pun, dan memiliki sejumlah lainnya hal-hal di antara mereka.

AmbroseChapel
sumber
Ah, ya, itu bahkan pertanyaan yang mendorong saya untuk menanyakan yang ini (tautan pertama).
Chas. Owens
16

Itu tergantung pada apa yang Anda maksud dengan "parsing". Secara umum, XML tidak dapat diuraikan menggunakan regex karena tata bahasa XML tidak berarti biasa. Sederhananya, regex tidak bisa menghitung (yah, Perl regex mungkin sebenarnya bisa menghitung hal-hal) sehingga Anda tidak bisa menyeimbangkan tag buka-tutup.

Anton Gogolev
sumber
saya kira backreferences dapat menyelesaikan masalah tag buka dan tutup
Rishul Matta
1
@RishulMatta: bagaimana? Anda hanya memiliki sejumlah referensi yang terbatas dan perhatikan bahwa Anda perlu membalikkan tag ... Selanjutnya definisi ketat dari regex tidak membolehkan referensi kembali.
Willem Van Onsem
.NET memungkinkan untuk menyeimbangkan ekspresi, yang muncul dan ditekan, dan secara teoritis dapat digunakan untuk mencocokkan hierarki. Tapi itu masih ide yang buruk.
Abel
9

Apakah orang benar-benar membuat kesalahan dengan menggunakan regex, atau apakah itu cukup baik untuk tugas yang mereka coba capai?

Saya sepenuhnya setuju bahwa parsing html dan xml menggunakan regex tidak dimungkinkan karena orang lain telah menjawab.

Namun, jika kebutuhan Anda bukan untuk mem-parsing html / xml tetapi untuk mendapatkan sedikit data dalam bit "html / xml" yang dikenal baik maka mungkin ekspresi reguler atau bahkan "substring" yang lebih sederhana sudah cukup baik.

Robin Day
sumber
7
Tentukan "cukup baik". Mau tidak mau regex sederhana tidak akan bekerja. Bukankah mencocokkan sesuatu atau mencocokkan sesuatu yang seharusnya tidak Anda bug? Jika demikian maka menggunakan regex adalah kesalahan. Parser HTML dan XML tidak sulit digunakan. Menghindari mempelajarinya adalah ekonomi palsu.
Chas. Owens
1
ok, tentukan "cukup baik". Katakanlah saya memiliki halaman web yang memberi tahu saya alamat IP klien. Hanya itu yang dilakukannya. Sekarang, saya perlu menulis aplikasi untuk mesin klien yang memberi tahu saya alamat IP-nya. Saya pergi ke situs itu, mencari alamat IP dan mengembalikannya. Parsing HTML tidak diperlukan!
Robin Day
2
Jika Anda memiliki string arbitrer yang formatnya sepenuhnya di bawah kendali Anda, fakta bahwa string tersebut merupakan XML yang benar-benar terbentuk benar-benar tidak relevan. Tetapi hampir tidak ada kasus penggunaan untuk XML benar-benar termasuk dalam kategori ini.
Robert Rossney
15
Saya dapat memberi tahu Anda dari pengalaman menyakitkan bahwa sebagian besar waktu mungkin untuk mendapatkan apa yang Anda inginkan menggunakan pola regex kompleks yang absurd. Sampai situs web mengalami perubahan kecil yang lucu dan Anda dapat membuang regex ini yang membuat Anda menangis selama dua hari di luar jendela dan mulai lagi.
Thomasz
@ Robert: "hampir tidak ada kasus penggunaan" adalah berlebihan. Dalam pengalaman saya ada kasus penggunaan yang cukup umum. YAGNI berlaku di sini ... kadang-kadang. Kuncinya adalah mengetahui bagaimana solusi antipeluru dan berumur panjang, untuk tugas khusus yang Anda tangani. Robin punya poin bagus. Dia hanya mengatakan bahwa parsing XML penuh tidak selalu sepadan ... yang benar bahkan jika Anda tahu cara menggunakannya.
LarsH
6

Orang biasanya default untuk menulis pola serakah, cukup sering mengarah ke un-dipikirkan-melalui. * Menyeruput potongan file besar ke dalam <foo>. * </foo> terbesar.

kekacauan
sumber
2
Selain membuat pengulangan malas .*?<, Anda bisa memperbaikinya dengan menggunakan kelas karakter yang dinegasikan seperti [^<]*<. (Penafian: jelas itu masih tidak mudah, yang merupakan inti dari pertanyaan.)
Rory O'Kane
6

Saya tergoda untuk mengatakan "jangan menciptakan kembali roda". Kecuali bahwa XML benar-benar, sangat kompleks. Jadi mungkin saya harus mengatakan "jangan menemukan kembali synchrotron."

Mungkin klise yang benar dimulai "ketika semua yang Anda miliki adalah palu ..." Anda tahu bagaimana menggunakan ekspresi reguler, ekspresi reguler bagus untuk parsing, jadi mengapa repot-repot belajar parsing pustaka XML?

Karena parsing XML itu sulit . Upaya apa pun yang Anda hemat dengan tidak harus belajar menggunakan parsing pustaka XML akan lebih dari sekadar dibuat oleh jumlah karya kreatif dan bug-swatting yang harus Anda lakukan. Demi kepentingan Anda sendiri, google "perpustakaan XML" dan memanfaatkan karya orang lain.

Isaac Rabinovitch
sumber
3
Ini tidak serumit C ++.
Cole Johnson
6
@Cole "Cole9" Johnson Saya tidak akan menggunakan RE untuk mem-parsing C ++ juga.
Isaac Rabinovitch
2
Jika XML adalah sebuah synchrotron, C ++ akan menjadi Large Hadron Collider.
Kevin Kostlan
4

Saya percaya klasik ini memiliki informasi yang Anda cari. Anda dapat menemukan poin di salah satu komentar di sana:

Saya pikir kekurangannya di sini adalah bahwa HTML adalah tata bahasa Chomsky Type 2 (tata bahasa bebas konteks) dan RegEx adalah tata bahasa Chomsky Type 3 (ekspresi reguler). Karena tata bahasa Tipe 2 pada dasarnya lebih kompleks daripada tata bahasa Tipe 3 - Anda tidak mungkin berharap untuk membuat ini berhasil . Tetapi banyak yang akan mencoba, beberapa akan mengklaim sukses dan yang lain akan menemukan kesalahan dan benar-benar mengacaukan Anda.

Beberapa info lagi dari Wikipedia: Chomsky Hierarchy

Adam Arold
sumber
6
"Ekspresi reguler" tidak memiliki arti yang persis sama dalam diskusi tata bahasa formal seperti di sini. Kebanyakan mesin regex yang ada lebih kuat daripada 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
4

Saya pikir masalahnya bermuara pada:

  1. Regex hampir selalu salah. Ada input yang sah yang tidak akan cocok dengan benar. Jika Anda bekerja cukup keras, Anda dapat membuatnya 99% benar, atau 99,999%, tetapi menjadikannya 100% benar hampir tidak mungkin, jika hanya karena hal-hal aneh yang memungkinkan XML dengan menggunakan entitas.

  2. Jika regex salah, bahkan untuk 0,00001% input, maka Anda memiliki masalah keamanan, karena seseorang dapat menemukan satu input yang akan merusak aplikasi Anda.

  3. Jika regex cukup benar untuk mencakup 99,99% dari kasus maka itu akan benar-benar tidak dapat dibaca dan tidak dapat dipelihara.

  4. Sangat mungkin bahwa regex akan berkinerja sangat buruk pada file input berukuran sedang. Pertemuan pertama saya dengan XML adalah untuk mengganti skrip Perl yang (secara tidak benar) mem-parsing dokumen XML yang masuk dengan parser XML yang tepat, dan kami tidak hanya mengganti 300 baris kode yang tidak dapat dibaca dengan 100 baris yang dapat dipahami siapa pun, tetapi kami meningkatkan waktu respons pengguna dari 10 detik hingga sekitar 0,1 detik.

Michael Kay
sumber
1

Secara umum, XML tidak dapat diuraikan menggunakan regex karena tata bahasa XML tidak berarti biasa. Sederhananya, regex tidak bisa menghitung (yah, Perl regex mungkin sebenarnya bisa menghitung hal-hal) sehingga Anda tidak bisa menyeimbangkan tag buka-tutup.

Saya tidak setuju. Jika Anda akan menggunakan rekursif dalam regex, Anda dapat dengan mudah menemukan tag buka dan tutup.

Di sini saya menunjukkan contoh regex untuk menghindari kesalahan parsing contoh dalam pesan pertama.

Maxim Suslov
sumber
Pertama, regex rekursif bukan ekspresi reguler (jika Anda melihat dalam tanda kurung, Anda akan melihat bahwa saya mengakui bahwa regex Perl, yang rekursif, dapat menghitung hal-hal, yang diperlukan untuk menangani HTML). Kedua, contoh Anda adalah untuk XHTML atau XML yang terbentuk dengan baik. HTML tidak terbentuk dengan baik. Ketiga, Anda harus bertanya pada diri sendiri, apakah lebih mudah untuk memperluas dan memelihara parser yang ditulis dalam bahasa regex rekursif atau bahasa pemrograman tujuan umum.
Chas. Owens
Keempat, bahkan contoh Anda sepele rusak sementara masih menjadi XML yang valid. Tambahkan satu ruang antara content_block dan id dan gagal. Saya yakin jika saya menghabiskan beberapa menit lagi saya akan menemukan beberapa kesalahan struktural lainnya dalam kode Anda. Itu bukan ide yang bagus.
Chas. Owens
1

Saya memberikan jawaban yang disederhanakan untuk masalah ini di sini . Meskipun tidak memperhitungkan tanda 100%, saya menjelaskan bagaimana itu mungkin jika Anda bersedia melakukan beberapa pekerjaan pra-pemrosesan.

Erutan409
sumber