XML ve HTML'yi neden normal ifadeyle ayrıştırmanın zor olduğuna dair bazı örnekler verebilir misiniz? [kapalı]


402

Bir hata İnsanların yaparken görmek üzerinde ve tekrar tekrar bir regex ile XML veya HTML ayrıştırmak çalışıyor. XML ve HTML'yi ayrıştırmanın zor nedenlerinden bazıları şunlardır:

İnsanlar bir dosyayı bir satır sırası olarak ele almak ister, ancak bu geçerlidir:

<tag
attr="5"
/>

İnsanlar <veya <etiketini etiketin başlangıcı olarak ele almak ister, ancak vahşi doğada bunun gibi şeyler bulunur:

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

İnsanlar genellikle başlangıç ​​etiketlerini bitiş etiketleriyle eşleştirmek isterler, ancak XML ve HTML etiketlerin kendilerini içermesine izin verir (geleneksel regex'ler hiç işleyemez):

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

İnsanlar genellikle bir belgenin içeriğiyle (ünlü "belirli bir sayfada tüm telefon numaralarını bul" sorunu) eşleşmek ister, ancak veriler işaretlenebilir (görüntülendiğinde normal gibi görünse bile):

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

Yorumlar kötü biçimlendirilmiş veya eksik etiketler içerebilir:

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

Başka hangi yakaların farkındasınız?


14
Web tarayıcıları saniyede milyonlarca kez bu tür karışıklıklara mantıklı geliyor, birisi bizim için sadece ölümlüler için bir web sayfası ayrıştırıcı sınıfı oluşturamıyor mu?
Jon Winstanley

24
Jon, var. Perl'de birçok HTML :: Ayrıştırıcı, HTML :: TreeBuilder vb. Vardır. Diliniz için neredeyse kesinlikle bir tane vardır.
Chas. Owens

12
En iyi cevap, stackoverflow.com/a/1732454/135078 (Zalgo'ya dikkat edin)
Kelly

3
[[X] HTML'yi normal ifadeyle ayrıştıramıyorsunuz] [1] [1]: stackoverflow.com/a/1732454/468725
Pavel P

4
İşte nasıl iyi bir açıklama kesinlikle olabilir desenleri ile ayrıştırma HTML yanı neden olarak muhtemelen bunu yapmak istemiyoruz.
tchrist

Yanıtlar:


260

İşte sizin için bazı eğlenceli geçerli XML:

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

Ve bu küçük neşe paketi geçerli HTML'dir:

<!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>

Geçersiz yapılar için tarayıcıya özgü tüm ayrıştırmadan bahsetmiyoruz.

Buna karşı regex çukurluğu iyi şanslar!

EDIT (Jörg W Mittag): İşte iyi biçimlendirilmiş, geçerli bir HTML 4.01 güzel bir parça:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
  "http://www.w3.org/TR/html4/strict.dtd"> 
<HTML/
  <HEAD/
    <TITLE/>/
    <P/>

6
XML olanı? Orada birkaç farklı yapı var, hangisi zahmetli? DTD dahili alt kümesi? Bu yeni bir varlık tanımlar; 'y' olarak adlandırılır ve normalde tırnak içinde olmasa da dahili altkümeyi bitirecek bir ']>' dizisi içerir.
bobince

16
(Bu, bir DTD doğrulayıcı ayrıştırıcı
olmasanız

17
HTML örnekleri, nadiren bilinen bir özellikten yararlanır: kısa etiketler. W3.org/QA/2007/10/shorttags.html
netvope

25
Birisi yukarıda gösterildiği gibi HTML yazdığında Tim Berners-Lee tek bir gözyaşı döküyor.
fgysin Monica

5
Stackoverflow'un Sözdizimi vurgulayıcı "]" 1'inde başarısız nasıl seviyorum.
GlassGhost

71

Aslında

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

geçerli bir HTML değil ve geçerli bir XML de değil.

'<' Ve '>' özellik dizeleri içindeki geçerli karakterler olmadığından geçerli bir XML değil. Karşılık gelen XML varlıkları kullanılarak kaçmaları gerekir & lt; ve & gt;

HTML'de kısa kapatma formuna izin verilmediğinden (ancak XML ve XHTML'de doğrudur) geçerli bir HTML değildir. 'İmg' etiketi, HTML 4.01 spesifikasyonuna göre örtük olarak kapalı bir etikettir. Bu, manuel olarak kapatmanın gerçekten yanlış olduğu ve diğer tüm etiketleri iki kez kapatmaya eşdeğer olduğu anlamına gelir.

HTML'deki doğru sürüm

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

ve XHTML ve XML'deki doğru sürüm

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

Verdiğiniz aşağıdaki örnek de geçersiz

<
tag
attr="5"
/>

Bu geçerli bir HTML veya XML değil. Nitelikler ve kapanış '>' istedikleri yerde olsa da, etiketin adı '<' işaretinin hemen arkasında olmalıdır. Geçerli XML aslında

<tag
attr="5"
/>

Ve işte başka bir komik olanı: "veya" karakterini özellik alıntılama karakteriniz olarak kullanmayı seçebilirsiniz.

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

Gönderilen diğer tüm nedenler doğrudur, ancak HTML'yi ayrıştırmayla ilgili en büyük sorun, insanların genellikle tüm sözdizimi kurallarını doğru bir şekilde anlamadığıdır. Tarayıcınızın etiket çıkışınızı HTML olarak yorumlaması, geçerli HTML yazdığınız anlamına gelmez.

Edit: Ve hatta stackoverflow.com geçerli ve geçersiz tanımı ile benimle aynı fikirde. Düzeltilmiş sürümüm varken geçersiz XML / HTML'niz vurgulanmaz.

Temel olarak, XML normal ifadelerle ayrıştırılamaz. Ancak bunun için de bir neden yok. Her dil için çok sayıda XML ayrıştırıcısı vardır. SAX ayrıştırıcılar, DOM ayrıştırıcılar ve Çekmeli ayrıştırıcılar arasında seçim yapabilirsiniz. Tüm bunların bir normal ifade ile ayrıştırmaktan çok daha hızlı olması garanti edilir ve daha sonra elde edilen DOM ağacında XPath veya XSLT gibi harika teknolojiler kullanabilirsiniz.

Bu nedenle cevabım: XML'yi regexps ile ayrıştırmakla kalmıyor, aynı zamanda kötü bir fikir. Milyonlarca mevcut XML ayrıştırıcısından birini kullanın ve XML'in tüm gelişmiş özelliklerinden yararlanın.

HTML, kendi başınıza ayrıştırmayı denemek için çok zor. Birincisi, yasal sözdiziminin farkında olmadığınız birçok küçük inceliği vardır ve ikincisi, vahşi HTML HTML'nin sadece büyük bir kokuşmuş yığınıdır (sapmamı alırsınız). Etiket çorbası gibi HTML'yi işlemede iyi bir iş yapan çeşitli gevşek ayrıştırıcı kütüphaneleri var, sadece bunları kullanın.


8
Yine de> gibi> kaçmanıza gerek yok.
Joey

8
Tamam, s / geçerli / wild / g
Chas içinde var. Owens

1
Aslında, spesifikasyona göre> olduğu gibi kaçmanız gerekir, tıpkı <gibi <& <& & amp; ve "as" ve "as" gibi özelliklerde bu kadar çok ayrıştırıcı vardır
LordOfThePigs

19
Spesifikasyon, içerikteki ']]>' dizisinin özel durumu hariç, '>' karakterinden kaçınması gerektiğini söylemez. Bu nedenle her zaman '>' 'dan kaçmak en kolay yoldur, ancak spec tarafından gerekli değildir.
bobince


56

Bu konuyla ilgili tüm bir blog yazısı yazdım: Normal İfade Sınırlamaları

Sorunun temel noktası, HTML ve XML'nin düzgün ayrıştırmak için sayma mekanizmaları gerektiren özyinelemeli yapılardır. Gerçek bir normal ifade sayılamayabilir. Saymak için bağlamsız bir dilbilgisine sahip olmalısınız.

Önceki paragraf hafif bir uyarı ile gelir. Belirli regex uygulamaları artık özyineleme fikrini desteklemektedir. Bununla birlikte, normal ifadelerinize özyineleme eklemeye başladığınızda, gerçekten sınırları genişletiyorsunuz ve bir ayrıştırıcıyı düşünmelisiniz.


20

Listenizde olmayan bir özellik, niteliklerin herhangi bir sırada görünebileceğidir, bu nedenle regex'iniz href "foo" ve sınıf "bar" ile bir bağlantı arıyorsa, herhangi bir sırada gelebilir ve herhangi bir sayıda aralarındaki şeyler.


Ah, evet, bu soruyu sormamı isteyen soru bile vardı (ilk bağlantı).
Chas. Owens

16

"Ayrıştırma" ile ne demek istediğinize bağlıdır. Genel olarak konuşursak, XML dilbilgisi hiçbir şekilde düzenli olmadığından XML regex kullanılarak ayrıştırılamaz. Basitçe ifade etmek gerekirse, normal ifadeler sayamaz (iyi, Perl normal ifadeleri aslında bir şeyleri sayabilir), böylece açma-kapama etiketlerini dengeleyemezsiniz.



1
@RishulMatta: nasıl? Yalnızca sınırlı sayıda backreferences var ve etiketleri tersine çevirmeniz gerektiğini unutmayın ... Ayrıca normal ifadelerin katı tanımı backreferences'a izin vermez.
Willem Van Onsem

.NET, pop ve push ifadelerinin dengelenmesine izin verir ve teorik olarak hiyerarşiyi eşleştirmek için kullanılabilir. Ama yine de kötü bir fikir.
Abel

9

İnsanlar normalde bir normal ifade kullanarak hata mı yapıyorlar yoksa elde etmeye çalıştıkları görev için yeterince iyi mi?

Tamamen html ve xml regex kullanarak ayrıştırma diğer insanlar cevap gibi mümkün olmadığını kabul ediyorum.

Ancak, gereksinim html / xml ayrıştırmak değil, sadece html / xml "bilinen iyi" bir bit veri küçük bir bit almak için ise, o zaman belki de düzenli bir ifade, hatta daha basit bir "alt dize" yeterlidir.


7
"Yeterince iyi" tanımlayın. Kaçınılmaz olarak, basit normal ifade çalışmaz. Hata yapmamanız gereken bir şey mi yoksa bir şey mi eşleşmiyor? Eğer öyleyse regexes kullanmak bir hatadır. HTML ve XML ayrıştırıcılarının kullanımı zor değildir. Onları öğrenmekten kaçınmak yanlış bir ekonomidir.
Chas. Owens

1
tamam, "yeterince iyi" yi tanımla. Diyelim ki bana IP adresini söyleyen bir web sayfam var. Tüm yaptığı bu. Şimdi, istemciler makine için IP adresini söyleyen bir uygulama yazmam gerekiyor. Bu siteye gidiyorum, bir IP adresi arıyorum ve iade ediyorum. HTML'yi ayrıştırmaya gerek yoktur!
Robin Day

2
Biçimi tamamen kontrolünüz altında olan rastgele bir dizeniz varsa, dizenin iyi biçimlendirilmiş XML olduğu gerçeği gerçekten alakalı değildir. Ancak XML için neredeyse hiçbir kullanım durumu aslında bu kategoriye girmez.
Robert Rossney

15
Acı verici deneyimlerden, çoğu zaman saçma karmaşık regex kalıplarını kullanarak istediğinizi elde etmenin mümkün olduğunu söyleyebilirim. Web sitesi komik bir küçük değişiklik geçirene kadar ve iki gün boyunca pencereden ağlamak ve yeniden başlamak yapan bu normal ifade atabilirsiniz.
Thomasz

@Robert: "neredeyse hiç kullanım vakası" abartıdır. Deneyimlerime göre, yeterince yaygın kullanım örnekleri vardır. YAGNI burada geçerlidir ... bazen. İşin püf noktası, ele aldığınız belirli bir görev için çözümünüzün ne kadar kurşun geçirmez ve uzun ömürlü olduğunu bilmek. Robin'in iyi bir anlamı var. Sadece tam XML ayrıştırma her zaman buna değmez diyor ... nasıl kullanılacağını bilseniz bile bu doğrudur.
LarsH

6

İnsanlar genellikle açgözlü kalıplar yazmayı varsayılan olarak yaparlar, genellikle yeterince düşünülemezler.


2
Tekrarı tembel hale getirmenin yanı sıra .*?<, bunu olumsuzlanmış bir karakter sınıfı kullanarak düzeltebilirsiniz [^<]*<. (Feragat: Açıkçası bu hala kusursuz değildir, bu da sorunun ana konusudur.)
Rory O'Kane

6

"Tekerleği yeniden icat etme" demeye cazip geldim. Bunun dışında XML gerçekten çok karmaşık bir formattır. Belki de "senkrotronu yeniden icat etme" demeliyim.

Belki de doğru klişe başlar "sahip olduğunuz tek şey bir çekiç ..." Düzenli ifadeleri nasıl kullanacağınızı biliyorsunuz, düzenli ifade ayrıştırmada iyi, neden bir XML ayrıştırma kütüphanesi öğrenmek için uğraşasınız ki?

Çünkü XML ayrıştırmak zordur . Bir XML ayrıştırma kitaplığı kullanmayı öğrenmek zorunda kalmadan kaydettiğiniz her türlü çaba, yapmanız gereken yaratıcı çalışma ve hata azaltma miktarından daha fazla olacaktır. Kendi iyiliğiniz için Google "XML kitaplığı" nı kullanın ve başka birinin çalışmalarından yararlanın.


3
Gerçi C ++ kadar karmaşık değil.
Cole Johnson

6
@Cole "Cole9" Johnson Ben de C ++ ayrıştırmak için REs kullanmaz.
Isaac Rabinovitch

2
XML bir senkrotron ise, C ++ Büyük Hadron Çarpıştırıcısı olur.
Kevin Kostlan

4

Bu klasiğin aradığınız bilgiye sahip olduğuna inanıyorum . Buradaki yorumlardan birinde noktayı bulabilirsiniz:

Buradaki kusurun HTML'nin Chomsky Type 2 dilbilgisi (bağlamsız dilbilgisi) ve RegEx'in Chomsky Type 3 dilbilgisi (normal ifade) olduğunu düşünüyorum. Bir Tip 2 dilbilgisi temel olarak bir Tip 3 dilbilgisinden daha karmaşık olduğundan, bu işi yapmayı umut edemezsiniz . Ama birçoğu deneyecek, bazıları başarı iddia edecek ve diğerleri hatayı bulacak ve sizi tamamen mahvedecek.

Wikipedia'dan biraz daha bilgi: Chomsky Hierarchy


6
"Düzenli ifade" resmi dilbilgisi tartışmalarında burada olduğu gibi tam olarak aynı anlama gelmez. Çoğu normal regex motorları Chomsky Type 3 gramerlerinden daha güçlüdür (örneğin açgözlü olmayan eşleştirme, backrefs). Bazı normal regex motorları (Perl'ler gibi) Turing tamamlandı. Bunların bile HTML'yi ayrıştırmak için zayıf araçlar olduğu doğrudur, ancak bu alıntılanan argüman bunun nedeni değildir.
dubiousjim

4

Bence sorunlar şu şekilde kayboluyor:

  1. Normal ifade neredeyse her zaman yanlıştır. Doğru şekilde eşleşemeyeceği meşru girdiler vardır. Yeterince sıkı çalışıyorsanız, bunu% 99 doğru veya% 99,999 yapabilirsiniz, ancak yalnızca% 100 doğru yapmak, XML'in varlıkları kullanarak izin verdiği tuhaf şeyler nedeniyle neredeyse imkansızdır.

  2. Normal ifade girişlerin% 0.00001'i için bile yanlışsa, o zaman bir güvenlik sorununuz vardır, çünkü birisi uygulamanızı kıracak bir girişi bulabilir.

  3. Normal ifade vakaların% 99,99'unu kapsayacak kadar doğruysa, tamamen okunamayacak ve sürdürülemez olacaktır.

  4. Normal ifadenin, orta boyutlu girdi dosyalarında çok kötü performans göstermesi çok olasıdır. XML ile ilk karşılaşmam, gelen XML belgelerini uygun bir XML ayrıştırıcıyla (yanlış) ayrıştıran bir Perl betiğini değiştirmekti ve sadece 300 satır okunamayan kodu herkesin anlayabileceği 100 satırla değiştirmedik, ancak kullanıcı yanıt süresini iyileştirdik 10 saniye ila 0.1 saniye arasındadır.


1

Genel olarak konuşursak, XML dilbilgisi hiçbir şekilde düzenli olmadığından XML regex kullanılarak ayrıştırılamaz. Basitçe ifade etmek gerekirse, normal ifadeler sayamaz (iyi, Perl normal ifadeleri aslında bir şeyleri sayabilir), böylece açma-kapama etiketlerini dengeleyemezsiniz.

Katılmıyorum. Normal ifadede özyinelemeli kullanacaksanız, açık ve kapalı etiketleri kolayca bulabilirsiniz.

Burada , ilk iletideki örneklerin hatalarını ayrıştırmamak için normal ifade örneğini gösterdim.


İlk olarak, özyinelemeli regexes düzenli ifadeler değildir (parantez içine bakarsanız, Perl'in özyinelemeli olan regex'lerinin HTML'yi işlemek için gerekli olan şeyleri sayabileceğini kabul edeceğim). İkinci olarak, örneğiniz iyi biçimlendirilmiş XHTML veya XML içindir. HTML iyi biçimlendirilmemiş. Üçüncüsü, kendinize sormanız gerekir, özyinelemeli regex dilinde veya genel amaçlı bir programlama dilinde yazılmış bir ayrıştırıcıyı genişletmek ve korumak daha kolaydır.
Chas. Owens

Dördüncüsü, örneğiniz bile geçerli XML iken önemsiz bir şekilde kırılmıştır. Content_block ve id arasına bir boşluk ekleyin ve başarısız olur. Birkaç dakika daha geçirirsem, kodunuzda başka bir yapısal hata bulurdum. Sadece iyi bir fikir değil.
Chas. Owens

1

Burada bu soruna basitleştirilmiş bir cevap verdim . % 100 işaretini hesaba katmasa da, bazı ön işleme işleri yapmak isteyip istemediğinizi nasıl açıklayacağım.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.