RegEx, XHTML bağımsız etiketleri hariç açık etiketleri eşleştirir


1473

Tüm bu açılış etiketlerini eşleştirmek gerekir:

<p>
<a href="foo">

Ama bunlar değil:

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

Bunu buldum ve doğru anladığımdan emin olmak istedim. Sadece a-z.

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

İnanıyorum ki diyor:

  • O zamandan daha azını bul
  • Az'ı bir veya daha fazla kez bulun (ve yakalayın), sonra
  • Sıfır veya daha fazla boşluk bulun, ardından
  • /O zaman hariç , herhangi bir karakteri sıfır veya daha fazla kez, açgözlü, bulun
  • Daha büyük bir değer bulun

Buna hakkım var mı? Ve daha da önemlisi, ne düşünüyorsunuz?

Yanıtlar:


4417

[X] HTML'yi normal ifadeyle ayrıştıramazsınız. Çünkü regex ile HTML ayrıştırılamıyor. Normal ifade, HTML'yi doğru şekilde ayrıştırmak için kullanılabilecek bir araç değildir. Daha önce burada defalarca HTML ve regex sorularına cevap verdiğim gibi, regex kullanımı HTML tüketmenize izin vermeyecektir. Düzenli ifadeler, HTML tarafından kullanılan yapıları anlamak için yeterince karmaşık olmayan bir araçtır. HTML normal bir dil değildir ve bu nedenle normal ifadelerle ayrıştırılamaz. Normal ifade sorguları, HTML'yi anlamlı parçalarına ayıracak şekilde donatılmamıştır. pek çok kez ama bana ulaşmıyor. Perl tarafından kullanılan gelişmiş düzensiz düzenli ifadeler bile HTML'yi ayrıştırma görevine bağlı değildir. Beni asla çatlatmayacaksın. HTML, normal ifadelerle ayrıştırılamayacak kadar karmaşık bir dildir. Jon Skeet bile HTML'yi düzenli ifadeler kullanarak ayrıştıramaz. HTML'yi düzenli ifadelerle ayrıştırmaya çalıştığınızda, kutsal olmayan çocuk bakirelerin kanını ağlar ve Rus hackerlar web uygulamanızı alır. HTML'yi normal ifade ile ayrıştırmak, lekeli ruhları canlıların dünyasına çağırır. HTML ve normal ifade aşk, evlilik ve ritüel bebek öldürme gibi bir araya gelir. <center> onu geç kaldıramaz. Aynı kavramsal alanda regex ve HTML'nin gücü, zihninizi çok sulu macun gibi yok edecektir. HTML'yi regex ile ayrıştırırsanız, Onlara ve onların adlarını Temel Çok Dilli Düzlemde ifade edilemeyen Kişi için insanlık dışı bir işe mahkum eden küfür yollarına girersiniz. HTML-plus-regexp, gözlemlediğinizde ruhun sinirlerini canlandıracak, ruhunuz dehşet saldırısında soluyor.çok geç artık çok geç bir chi͡ld'in düzenlenmesi kurtarılamıyor regex'in tüm canlı dokuları tüketmesini sağlar (daha önce kehanet edildiği gibi HTML hariç) sevgili lord bize nasıl kimse bu ayrıştırma regex kullanarak ayrıştırmak için hayatta olabilir HTML, insanlığı, HTML'yi işlemek için bir araç olarak rege x kullanarak sonsuz işkence ve güvenlik açıklarına sonsuzluğa mahkum etti ve bu dünya ile korkunç varlıkların (SGML varlıkları gibi, ancak daha fazla bozuk) korkunç bir alanı arasında reg dünyası HTML ex ayrıştırıcıları olacak ins rı değerlendirilerek taşıma ap rogrammer bilinci i aw nGoogle'a orl d durmayan çığlık, o gelir, Pestilent sl ithy regex enfeksiyon wil sizin HT bitirip l Visual Basic gibi her zaman için ML ayrıştırıcı, uygulama ve varoluş sadece daha kötü geliyor diye com es yapamaz fi GHT h e geliyor, selam lar kutsal olmayan Radiance de stro҉ying tüm aydınlanma, HTML etiketlerini sızdıran fr̶ǫm yo ur gözleri gibi liq uid p ain, düzenli exp yeniden bir şarkı ekranı Seçilen ayrıştırma exti edecek mor seslerini nguish sp tal adam burada ben Gördüğünüz görebileceğiniz buna güzel t o f inal snuffing o f yalan Man TÜM Lost ait s LL I SLİnci OST e o gelecek midilli o com s o co es me ler t o ich veya permeat es al l MY FAC E yüzümü ᵒh tanrı n o HAYIR NoO Ç AÇIK Θ durdurma t o bir * ̶͑̾̾ gl es ͎a̧͈͖r̽̾̈́͒͑en ot rè̑ͧ̌aͨl̘̝̙̃ͤ͂̾̆ ZA̡͊͠͝LGΌ ISͮ̂҉̯͈͕̹̘̱ T O͇̹̺ͅƝ̴ȳ̳ TH̘ Ë͖́̉ ͠P̯͍̭O̚ N̐Y̡ H̸̡̪̯ͨ͊̽̅̾̎Ȩ̬̩̾͛ͪ̈́̀́͘ ̶̧̨̱̹̭̯ͧ̾ͬC̷̙̲̝͖ͭ̏ͥͮ͟Oͮ͏̮̪̝͍M̲̖͊̒ͪͩͬ̚̚͜Ȇ̴̟̟͙̞ͩ͌͝ S̨̥̫͎̭ͯ̿̔̀ͅ


Bunun yerine bir XML ayrıştırıcı kullanmayı denediniz mi?


Moderatör Notu

Bu yayın, içeriğinde uygunsuz düzenlemeler yapılmasını önlemek için kilitlendi. Gönderi tam olarak göründüğü gibi görünüyor - içeriğiyle ilgili herhangi bir sorun yok. Lütfen dikkatimiz için işaretlemeyin.


179
Kobi: Bence HTML Regex Memuru ile Ayrıştırma Yardımcısı görevinden çıkma zamanı. Kaç kere söylersek söylesin, her gün gelmeyi bırakmazlar ... her saat bile. Başka birinin biraz savaşabileceği kayıp bir nedendir. Yani devam ederseniz HTML'yi regex ile ayrıştırın. Sadece kırık kod, hayat ve ölüm değil.
bobince

27
Bu cevabı ayrıştırmak için RegEx kullanmak mümkün müdür?
Chris Porter

2
Bu yayını göremiyorsanız, tüm ihtişamıyla bir ekran görüntüsü: imgur.com/gOPS2.png
Andrew Keeton

3249

İken keyfi HTML sadece regex imkansızdır ile, bazen bir ayrıştırma için bunları kullanmak için uygun olan sınırlı bilinen HTML kümesi.

Veriyi kazımak istediğiniz ve daha sonra bir veritabanına aktarmak istediğiniz küçük bir HTML sayfası kümeniz varsa, normal ifadeler düzgün çalışabilir. Örneğin, yakın zamanda Parlamentonun web sitesinden aldığım Avustralya federal Temsilcilerinin adlarını, partilerini ve bölgelerini almak istedim. Bu sınırlı, tek seferlik bir işti.

Regexes benim için iyi çalıştı ve kurulumu çok hızlıydı.


131
Ayrıca, büyük belgelerden oldukça düzenli olarak biçimlendirilmiş verilerin kazınması, herhangi bir genel ayrıştırıcıdan daha akıllıca tarama ve regex kullanımı ile daha hızlı olacaktır. Ve regexes kodlama konusunda rahatsanız, kodlamayı xpath'lerden daha hızlı yapın. Ve kazıdığınız şeydeki değişikliklere neredeyse kesinlikle daha az kırılgan. Bu yüzden bleh.
Michael Johnston

255
@MichaelJohnston "Daha az kırılgan" mı? Neredeyse kesinlikle değil. Normal ifadeler, bir XML ayrıştırıcısının sessizce göz ardı edebileceğinden daha çok metin biçimlendirme ayrıntılarına önem verir. &foo;Kodlamalar ve CDATAbölümler arasında geçiş mi yapıyorsunuz? Belgenizde tarayıcının oluşturmadığı tüm boşlukları kaldırmak için HTML küçültücü mü kullanıyorsunuz? XML ayrıştırıcısı umursamaz ve iyi yazılmış bir XPath deyimi de sağlamaz. Diğer taraftan normal ifade tabanlı "ayrıştırıcı" ...
Charles Duffy

41
@CharlesDuffy bir defalık iş için sorun değil ve boşluklar için \ s + kullanıyoruz
kuantum

68
@xiaomao gerçekten de, zamanın geri kalanında başarısız olan% 80'lik bir çözüm elde etmek için tüm gotchas ve geçici çözümleri bilmek zorunda kalırsanız, "sizin için çalışır", sizi durduramıyorum. Bu arada, sözdizimsel olarak geçerli XML'in% 100'ünde çalışan ayrıştırıcılar kullanarak çitin yanımdayım.
Charles Duffy

374
Bir keresinde ~ 10k sayfalarından bazı verileri çekmek zorunda kaldım, hepsi aynı HTML şablonuyla. Ayrıştırıcıların boğulmasına neden olan HTML hataları ile doluydu ve tüm stilleri satır içi veya <font>vb. İdi: DOM'da gezinmeye yardımcı olacak sınıflar veya kimlikler yok. Bütün gün "doğru" yaklaşımla savaştıktan sonra, sonunda normal bir çözüm buldum ve bir saat içinde çalışmasını sağladım.
Paul A Jungwirth

2037

Buradaki kusurun HTML'nin Chomsky Type 2 dilbilgisi ( bağlamsız dilbilgisi) ve RegEx'in Chomsky Type 3 dilbilgisi (normal dilbilgisi) olduğunu düşünüyorum . Tip 2 dilbilgisi temel olarak Tip 3 dilbilgisinden daha karmaşık olduğundan (bkz. Chomsky hiyerarşisi ) XML'i RegEx ile ayrıştırmak matematiksel olarak imkansızdır .

Ama birçoğu deneyecek, bazıları başarı bile talep edecek - ama diğerleri hatayı bulana ve sizi tamamen mahvedinceye kadar.


225
OP, XHTML: start etiketlerinin çok sınırlı bir alt kümesini ayrıştırmayı istiyor. (X) HTML'yi CFG yapan şey, diğer öğelerin başlangıç ​​ve bitiş etiketleri arasında (gramer kuralında olduğu gibi) öğelere sahip olma potansiyelidir A -> s A e. (X) HTML yok değil bu özelliğe sahip olan bir başlangıç etiketi: Bir başlangıç etiketi başka bir başlangıç etiketlerini içeremez. OP'nin ayrıştırmaya çalıştığı alt küme bir CFG değildir.
LarsH

101
CS Teoride, düzenli diller şunlardır bağlamdan-bağımsız dillerin tam alt kümesi, ancak ana akım programlama dillerinde düzenli ifade uygulamaları daha güçlüdür. Gibi noulakaz.net/weblog/2007/03/18/... açıklar, kesinlikle CS teoriden düzenli bir ifade gerçekleştirmek olamaz şeydir, tekli içinde asal sayılar kontrol edebilirsiniz "Normal ifadeler" olarak adlandırılan.
Adam Mihalcin

11
@eyelidlessness: aynı "sadece" tüm CFG'ler için geçerliyse, değil mi? Yani (X) HTML girişi iyi biçimlendirilmemişse, tam gelişmiş bir XML ayrıştırıcısı bile güvenilir şekilde çalışmaz. Belki de atıfta bulunduğunuz "(X) HTML sözdizimi hatalarına" örnek verirseniz, neyi daha iyi elde ettiğinizi anlarım.
LarsH

82
@AdamMihalcin kesinlikle haklı. Ç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

27
Bu, en "tam ve kısa" cevaptır. Bu biçimsel grammars ve dillerin temellerini ve umarım bazı matematik öğrenmeye yöneltir onlar polinom zamanda NP-görevleri çözme gibi umutsuz şeylere değil idin zaman olacak bu yüzden
mishmashru

1332

Bu adamları dinleme. Tamamen olabilir daha küçük parçalar halinde görevi bölerseniz regex ile bağlam bağımsız gramerler ayrıştırmak. Bunların her birini sırayla yapan bir komut dosyasıyla doğru deseni oluşturabilirsiniz:

  1. Durdurma Problemini çözün.
  2. Bir daire kare.
  3. Gezgin Satıcı Sorununu O (log n) veya daha düşük bir değerde çalışın. Bundan daha fazlası varsa, RAM'iniz bitecek ve motor duracaktır.
  4. Desen oldukça büyük olacaktır, bu yüzden rastgele verileri kayıpsız bir şekilde sıkıştıran bir algoritmaya sahip olduğunuzdan emin olun.
  5. Neredeyse orada - her şeyi sıfıra böl. Çantada keklik.

Son kısmı kendim bitirmedim, ama yakınlaştığımı biliyorum. Bir CthulhuRlyehWgahnaglFhtagnExceptionsebepten ötürü atmaya devam ediyor , bu yüzden onu VB 6'ya taşıyacağım ve kullanacağım On Error Resume Next. Duvarda yeni açılan garip kapıyı araştırdığımda kodla güncelleme yapacağım. Hmm.

PS Pierre de Fermat da nasıl yapılacağını anladı, ancak yazdığı kenar boşluğu kod için yeterince büyük değildi.


80
Sıfıra bölme, bahsettiğiniz diğerlerinden çok daha kolay bir sorundur. Düz kayan nokta aritmetiği yerine (herkes olması gereken ama hiç kimse olmayan) aralıklar kullanırsanız, bir şeyi [sıfır içeren] bir aralığa bölebilirsiniz. Sonuç, artı ve eksi sonsuzluk içeren bir aralıktır.
rjmunro

148
Fermat'ın küçük kenar boşluğu sorunu, modern metin düzenleme yazılımındaki yumuşak kenar boşluklarıyla çözüldü.
kd4ttc

50
Fermat'ın küçük kenar boşluğu sorunu yazı tipi boyutunu
heltonbiker

29
Bilginize: Fermat problemi vardır aslında 1995 yılında çözülmüş ve sadece bunu yapmak için 358 yıl matematikçiler sürdü.
jmiserez

10
Soğuk füzyondan elde edilen Brownian cırcırları kullanarak bu yapışkan sıfıra bölmeyi atlayabildim ... ancak sadece kozmolojik sabiti çıkardığımda işe yarıyor.
Tim Lehner

1072

Feragatname : seçeneğiniz varsa bir ayrıştırıcı kullanın. Bahsedilen...

HTML etiketlerini eşleştirmek için kullandığım normal ifade budur:

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

Mükemmel olmayabilir, ancak bu kodu çok fazla HTML üzerinden çalıştırdım . <a name="badgenerator"">Web'de görünen garip şeyleri bile yakaladığını unutmayın .

Bağımsız etiketlerle eşleşmediğini düşünüyorum, Kobi'nin olumsuz arkaplanını kullanmak istersiniz :

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

ya da eğer değilse ya da değilse birleştirin.

Downvoters için: Bu gerçek bir üründen çalışma kodu. Bu sayfayı okuyan herkesin HTML'de regexes kullanmanın sosyal olarak kabul edilebilir olduğu izlenimini alacağından şüpheliyim.

Dikkat : Bu normal ifadenin hala CDATA blokları, yorumları, kod ve stil öğeleri varlığında bozulduğunu belirtmeliyim. İyi haber şu ki, regex kullananlardan kurtulabilirsiniz ...


94
Ben evrensel olarak mükemmel olmamak için ağlamak daha aklı başında şeyler üzerinde çalışan bir şey ile gitmek istiyorum :-)
prajeesh kumar

55
Birisi HTML içinde CDATA kullanıyor mu?
Danubian Sailor

16
Bu nedenle, ayrıştırma sorununu yalnızca regexp ile çözmezsiniz, ancak ayrıştırıcının bir parçası olarak bu işe yarayabilir. Not: çalışan ürün iyi kod anlamına gelmez. Suç yok, ama bu endüstriyel programlama nasıl çalışıyor ve paralarını alıyor
mishmashru

32
Sizin düzenli ifade başlar çok kısa, geçerli HTML başarısız: <!doctype html><title><</title>. Basit '<!doctype html><title><</title>'.match(/<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>/g)döner ["<!doctype html>", "<title>", "<</title>"]gerektiği süre ["<title>", "</title>"].

2
sadece verilen örnekleri eşleştirmeye & eşleşmemeye çalışıyorsak, /<.([^r> Cialis[^> Cialis*)?>/g works :-) // javascript: '<p> <a href = "foo"> <br /> <hr class = "foo" />'.match
/<.([^r> Cialis[^>

506

Size Dünya'nın yuvarlak olduğunu söyleyecek insanlar var (ya da belki de garip kelimeler kullanmak istiyorlarsa Dünya'nın oblate bir küredir). Yalan söylüyorlar.

Düzenli İfadelerin özyinelememesi gerektiğini söyleyecek insanlar var. Sizi sınırlıyorlar. Seni boyun eğdirmeleri gerekiyor ve bunu cehalet içinde tutarak yapıyorlar.

Onların gerçekliğinde yaşayabilir veya kırmızı hapı alabilirsin.

Lord Marshal gibi (o Marshal .NET sınıfının bir akrabası mı?), Underverse Stack Based Regex- Verse'yi gördüm ve hayal edemeyeceğiniz güç bilgisiyle döndüm . Evet, sanırım onları koruyan bir ya da iki tane vardı, ama televizyonda futbol izliyorlardı, bu yüzden zor değildi.

Bence XML durumu oldukça basit. Kötü zihniniz tarafından anlaşılmasını kolaylaştırmak için base64 içinde söndürülen ve kodlanan RegEx (.NET sözdiziminde) şöyle olmalıdır:

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

Ayarlanacak seçenekler RegexOptions.ExplicitCapture. Aradığınız yakalama grubu ELEMENTNAME. Yakalama grubu ERRORboş değilse, ayrıştırma hatası oluştu ve Regex durdu.

İnsan tarafından okunabilir bir normal ifadeye dönüştürürken sorun yaşıyorsanız, bu yardımcı olacaktır:

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());
    }
}

Emin değilseniz, hayır, şaka yapmıyorum (ama belki yalan söylüyorum). Çalışacak. Test etmek için tonlarca birim test yaptım ve hatta uygunluk testlerini (bir kısmını) kullandım . Bu bir belirteçtir, tam gelişmiş bir ayrıştırıcı değildir, bu nedenle XML'i yalnızca bileşen belirteçlerine böler. DTD'leri ayrıştırmaz / entegre etmez.

Oh ... regex'in kaynak kodunu istiyorsanız, bazı yardımcı yöntemlerle:

bir xml veya tam düz regex'i tokenize etmek için regex


68
Tanrım, muazzam. En büyük sorum neden? Tüm modern dillerin XML ayrıştırıcıları olduğunu biliyorsunuz, değil mi? Tüm bunları 3 satırda yapabilir ve çalışacağından emin olabilirsiniz. Dahası, saf düzenli ifadenin muhtemelen bazı şeyleri yapamayacağını fark ediyor musunuz? Karma bir normal ifade / zorunlu kod ayrıştırıcı oluşturmadıysanız, ancak sahip olduğunuz gibi görünmüyorsa. Rastgele verileri de sıkıştırabilir misiniz?
Justin Morgan

113
@ Justin bir nedene ihtiyacım yok. Bu yapılabilir (ve yasadışı / ahlaksız değildi), bu yüzden yaptım. Aklımızda, kabul ettiğimiz (Napoleon Hill) dışında hiçbir sınırlama yoktur ... Modern diller XML'i ayrıştırabilir mi? Gerçekten mi? Ve bunun yasadışı olduğunu düşündüm! :-)
xanatos

76
Efendim, ikna oldum. Bu kodu sürekli hareket eden makinem için çekirdeğin bir parçası olarak kullanacağım - patent bürosundaki aptalların başvurumu reddetmeye devam ettiğine inanabiliyor musunuz? Onlara göstereceğim. Hepsini göstereceğim!
Justin Morgan

31
@Justin Yani bir Xml Ayrıştırıcı tanımı gereği ücretsiz, Regex değil mi? Çünkü bir Xml Ayrıştırıcı tanım gereği hatasız değilse, çökmesini sağlayan bir xml olabilir ve adım 0'a geri dönelim. "XML. Bazı "yasadışı" XML'leri ayrıştırabilirler. Hatalar her ikisini de çökertebilir. C # XmlReader bu Regex'ten kesinlikle daha fazla test edilmiştir.
xanatos

31
Hayır, hiçbir şey hatasız değildir: 1) Tüm programlar en az bir hata içerir. 2) Tüm programlar, en az bir gereksiz kaynak kodu satırı içerir. 3) # 1 ve # 2 ile ve mantıksal tümevarım kullanarak, herhangi bir programın bir hata ile tek bir kod satırına indirgenebileceğini kanıtlamak basit bir konudur. (Learning Perl'den)
Scott Weaver

299

Kabukta, sed kullanarak HTML'yi ayrıştırabilirsiniz :

  1. Turing.sed
  2. HTML ayrıştırıcısını yazma (ödev)
  3. ???
  4. Kar!

İlgili (neden normal ifade eşleşmesini kullanmamanız gerekir):


3
Korkarım şaka yapmadın, @kenorb. Lütfen soruyu ve kabul edilen cevabı bir kez daha okuyun. Bu, genel olarak HTML ayrıştırma araçlarıyla veya HTML ayrıştırma kabuğu araçlarıyla ilgili değildir, HTML'yi normal ifadeler yoluyla ayrıştırmakla ilgilidir.
Palec

1
Hayır, @Abdul. Tamamen, muhtemelen (matematiksel anlamda) imkansızdır.
1717 Palec

3
Evet, bu cevap iyi özetliyor, @Abdul. Bununla birlikte, regex uygulamalarının matematiksel anlamda gerçekten düzenli ifadeler olmadığını unutmayın - onları daha güçlü, genellikle Turing-complete (Tip 0 gramerlerine eşdeğer) yapan yapılara sahiptirler. Argüman bu gerçeği kırıyor, ancak regex'lerin asla böyle bir iş yapamayacakları anlamında hala geçerli.
Palec

2
Bu arada, bahsettiğim şaka, kenorb'un (radikal) düzenlemelerinden önce, özellikle revizyon 4, @Abdul'un bu cevabının içeriğiydi.
1717 Palec

3
Komik olan şey OP'nin regex kullanarak html'yi ayrıştırmasını istemediğidir. Normal ifadeyi kullanarak metni (HTML olur) eşleştirmesini istedi. Hangi mükemmel makul.
Paralife

274

XML ve özellikle HTML'yi ayrıştırmak için doğru aracın normal ifade motoru değil ayrıştırıcı olduğunu kabul ediyorum . Ancak, diğerlerinin de belirttiği gibi, bazen normal ifadeyi kullanmak daha hızlı, daha kolaydır ve veri biçimini biliyorsanız işi yapar.

Microsoft aslında .NET Framework'te Düzenli İfadeler için En İyi Yöntemler bölümüne sahiptir ve özellikle Giriş Kaynağını düşünün .

Normal İfadelerin sınırlamaları vardır, ancak aşağıdakileri düşündünüz mü?

.NET çerçevesi Dengeleme Grubu Tanımlarını desteklemesi bakımından düzenli ifadeler söz konusu olduğunda benzersizdir .

Bu nedenle XML'yi düzenli ifadeler kullanarak ayrıştırabileceğinize inanıyorum. Bununla birlikte, geçerli bir XML olması gerektiğini unutmayın ( tarayıcılar HTML'yi çok affediyor ve HTML içinde kötü XML sözdizimine izin veriyor ). "Dengeleme Grubu Tanımı", normal ifade motorunun PDA gibi davranmasına izin vereceğinden bu mümkündür.

Yukarıda belirtilen 1. maddeden alıntı:

.NET Düzenli İfade Motoru

Yukarıda tarif edildiği gibi düzgün şekilde dengelenmiş yapılar, normal bir ifade ile tarif edilemez. Ancak, .NET normal ifade motoru dengeli yapıların tanınmasına izin veren birkaç yapı sağlar.

  • (?<group>) - yakalanan sonucu ad grubuyla yakalama yığınına iter.
  • (?<-group>) - yakalama yığından ad grubuyla en fazla yakalamayı açar.
  • (?(group)yes|no) - isim grubuna sahip bir grup varsa evet kısmı ile eşleşir, aksi takdirde hiçbir parça ile eşleşmez.

Bu yapılar, .NET normal ifadesinin, yığın işlemlerinin basit sürümlerine izin vererek kısıtlı bir PDA'yı taklit etmesine izin verir: push, pop ve empty. Basit işlemler, sırasıyla artış, azalma ve sıfıra eşittir. Bu, .NET normal ifade motorunun bağlamdan bağımsız dillerin bir alt kümesini, özellikle de yalnızca basit bir sayaç gerektirenleri tanımasını sağlar. Bu da geleneksel olmayan .NET düzenli ifadelerinin düzgün şekilde dengelenmiş yapıları tanımasını sağlar.

Aşağıdaki normal ifadeyi düşünün:

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

Bayrakları kullanın:

  • Tek çizgi
  • IgnorePatternWhitespace (normal ifadeyi daraltırsanız ve tüm boşlukları kaldırırsanız gerekli değildir)
  • IgnoreCase (gerekli değil)

Düzenli İfade Açıklaması (satır içi)

(?=<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

Bunu A Better .NET Regular Expression Tester'da deneyebilirsiniz .

Örnek kaynağını kullandım:

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

Bu eşleşmeyi buldu:

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

aslında bu şekilde ortaya çıkmasına rağmen:

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

Son olarak, Jeff Atwood'un Html Cthulhu Yolu Ayrıştırma makalesinden gerçekten keyif aldım . Yeterince komik, şu anda 4k'den fazla oyu olan bu sorunun cevabını gösteriyor.


18
System.TextC # 'ın bir parçası değildir. Bu .NET'in bir parçasıdır.
John Saunders

8
Normal ifadenizin ( (?=<ul\s*id="matchMe"\s*type="square"\s*>) # match start with <ul id="matchMe"...) ilk satırında, "<ul" ile "id" arasında \s+, \s*<ulid = ...;)
C0deH4cker

@ C0deH4cker Doğru, ifadenin \s+yerine olmalıdır \s*.
Sam

4
Gerçekten anladığımdan değil, ama <img src="images/pic.jpg" />
regex'iniz

3
@Scheintod Yorum için teşekkürler. Kodu güncelledim. Önceki ifade, html'niz için başarısız olan bir /yere sahip olan kendi kendine kapanan etiketler için başarısız oldu <img src="images/pic.jpg" />.
Sam

258

Ben PHP XML ve HTML ayrıştırmak için QueryPath kullanmanızı öneririz . Temelde jQuery ile aynı sözdizimi, sadece sunucu tarafında.


8
@ Kyle — jQuery XML ayrıştırmaz, istemcinin yerleşik ayrıştırıcısını kullanır (varsa). Bu nedenle, bunu yapmak için jQuery'ye ihtiyacınız yoktur, ancak iki satır kadar düz eski JavaScript'e ihtiyacınız vardır . Yerleşik ayrıştırıcı yoksa, jQuery yardımcı olmaz.
RobG

1
@RobG Aslında jQuery yerleşik ayrıştırıcıyı değil DOM'yi kullanır.
Qix - MONICA SEÇİLDİ

11
@ Qix — belgelerin yazarlarına şunu söyleseniz iyi olur: " jQuery.parseXML tarayıcının yerel ayrıştırma işlevini kullanır… ". Kaynak: jQuery.parseXML ()
RobG

6
Buraya meme sorusundan ( meta.stackexchange.com/questions/19478/the-many-memes-of-meta/… ) geldikten sonra, cevaplardan birinin 'Use jQuery'
Jorn

221

HTML'yi normal ifadelerle ayrıştıramadığınız yanıtlar doğru olsa da, burada geçerli değildir. OP sadece bir HTML etiketini normal ifadelerle ayrıştırmak istiyor ve bu normal bir ifade ile yapılabilecek bir şey.

Bununla birlikte, önerilen normal ifade yanlıştır:

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

Eğer geriye tarafından, regex şey eklerseniz gibi aptalca şeyler maç zorunda olabilir <a >>, [^/]çok keyfi olduğunu. Ayrıca <space>*[^/]*gereksiz olduğunu unutmayın , çünkü [^/]*boşluklarla da eşleşebilir.

Benim önerim

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

(?<! ... )(Perl regex'lerinde) olumsuz arkaplan nerede . "A <, sonra bir kelime, sonra a> olmayan ve sonuncusu / olmayan ve ardından> olmayan her şeyi" okur.

Bunun <a/ >(orijinal regex gibi) şeylere izin verdiğini unutmayın, bu nedenle daha kısıtlayıcı bir şey istiyorsanız, boşluklarla ayrılmış özellik çiftlerini eşleştirmek için bir regex oluşturmanız gerekir.


29
Sorunun tam (X) HTML'yi ayrıştırmakla ilgili olmadığını, (X) HTML açık etiketlerini eşleştirmekle ilgili olduğunu belirttiğiniz için +1.
LarsH

10
Cevapların çoğunun görmezden geldiği başka bir şey, bir HTML ayrıştırıcısının HTML bölümleri için uygulanmasında düzenli ifadeleri çok iyi kullanabilmesidir ve çoğu ayrıştırıcı bunu yapmazsa şaşırırdım.
Thayne

@Thayne Kesinlikle. Etiketleri tek tek ayrıştırırken, normal ifade, iş için doğru araçtır. Makul bir cevap bulmak için sayfanın yarısına kadar kaydırılması oldukça saçmadır. Kabul edilen cevap yanlıştır çünkü lexing ve ayrıştırma işlemlerini karıştırır.
kasperd

2
Bir öznitelik değeri bir '>' veya '/' karakteri içerdiğinde burada verilen cevap başarısız olur.
Martin L

Bu, yorum veya CData bölümleri içeren HTML'de yanlış çalışır. Ayrıca, alıntı yapılan bir özellik bir >karakter içeriyorsa düzgün çalışmaz . Ben OP önermek Ne hemfikir olabilir bir regex ile yapılabilir, ancak burada sunulan bir basit etmek uzaktır.
JacquesB

183

Deneyin:

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

Sizinkine benzer, ancak sonuncusu >bir eğik çizgiden sonra olmamalı ve aynı zamanda kabul etmelidir h1.


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

21
Bu çok doğru ve ben bunu düşündüm, ama >sembolün & gt;
Kobi

65
>bir özellik değerinde geçerlidir. Gerçekten de, 'kanonik XML' serileştirmesinde kullanmamalısınız &gt;. ( >Bir özellik değerinde hiç de olağandışı bir şey olmadığını vurgulamak dışında, tamamen alakalı değildir.)
bobince

5
@Kobi: Regexp'de ünlem işareti (sonuna doğru yerleştirdiğiniz işaret) ne anlama geliyor?
Marco Demaio

6
@bobince: emin misin? Artık anlamıyorum, bu geçerli HTML de çok:<div title="this tag is a <div></div>">hello</div>
Marco Demaio

179

Eski bir Çinli stratejist, general ve filozof Sun Tzu şunları söyledi:

Düşmanlarınızı tanıyorsanız ve kendinizi tanıyorsanız, tek bir kayıp olmadan yüz savaş kazanabilirsiniz. Eğer sadece kendinizi tanıyorsanız, rakibinizi tanımıyorsanız, kazanabilir ya da kaybedebilirsiniz. Ne kendinizi ne de düşmanınızı tanıyorsanız, daima kendinizi tehlikeye atacaksınız.

Bu durumda düşmanınız HTML'dir ve ya kendiniz ya da normal ifadesiniz. Hatta düzensiz regex ile Perl bile olabilirsiniz. HTML bilmek. Kendini bil.

HTML'nin doğasını açıklayan bir haiku yazdım.

HTML has
complexity exceeding
regular language.

Ayrıca Perl'de normal ifadenin doğasını açıklayan bir haiku da oluşturdum.

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 );
    }
}

Çıktı:

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

Temel olarak, kendi kendine kapanan öğe düğümü adlarını tanımlayın, tüm html dizesini bir DOM kütüphanesine yükleyin, tüm öğeleri yakalayın, kendi kendine kapanmayan ve üzerinde çalışanları filtreleyin ve filtreleyin.

Eminim şimdiye kadar bu amaçla regex kullanmamanız gerektiğini zaten biliyorsunuzdur.


1
Gerçek XHTML ile uğraşıyorsanız getElementsByTagName öğesini ekleyin NSve ad alanını belirtin.
meder omuraliev

148

Bunun için tam ihtiyacınızı bilmiyorum, ancak .NET kullanıyorsanız, Html Agility Pack'i kullanamaz mısınız?

Alıntı:

Bu "web dışı" HTML dosyalarını ayrıştırmak için izin veren bir .NET kod kütüphanesidir. Ayrıştırıcı "gerçek dünya" hatalı biçimlendirilmiş HTML ile çok toleranslıdır.


137

İlk >önce bir a değil /. Bak nasıl yapılacağıyla ilgili ayrıntılar için buraya . Negatif gözetleme olarak adlandırılır.

Bununla birlikte, bunun naif bir uygulaması, <bar/></foo>bu örnek belgede eşleşecektir.

<foo><bar/></foo>

Çözmeye çalıştığınız sorun hakkında biraz daha bilgi verebilir misiniz? Etiketler aracılığıyla programlı olarak yineleme yapıyor musunuz?


1
Evet, eminim. Şu anda açık olan tüm etiketleri belirledikten sonra ayrı bir dizideki kapalı etiketlerle karşılaştırın. RegEx beynimi incitiyor.
Jeff

122

W3C, sahte regexp biçiminde ayrışmayı açıklar:
W3C Bağlantısı

İçin var bağlantıları izleyin QName, Sve Attributedaha net bir görüntü elde etmek.
Buna dayanarak, etiketleri sıyırma gibi şeyleri işlemek için oldukça iyi bir regexp oluşturabilirsiniz.


5
Bu bir psuedo normal ifade formu değil, bu bir EBNF formu, burada belirtildiği gibi: XML spesifikasyonu, ek 6
Rob G

106

PHP için buna ihtiyacınız varsa:

PHP DOM işlevleri düzgün XML biçimlendirilir sürece düzgün çalışmaz. İnsanlığın geri kalanı için kullanımları ne kadar iyi olursa olsun.

simplehtmldom iyi, ama biraz buggy buldum ve oldukça bellek ağır [Büyük sayfalarda çökecek.]

Sorguyolunu hiç kullanmadım , bu yüzden yararlılığı hakkında yorum yapamam.

Denemek için bir başka , kaynaklara çok hafif olan DOMParser'ım ve bir süredir mutlu bir şekilde kullanıyorum. Öğrenmesi kolay ve güçlü.

Python ve Java için benzer bağlantılar yayınlandı.

Downvoters için - Sınıfımı sadece XML ayrıştırıcılarının gerçek kullanıma dayanamayacağını kanıtladığında yazdım. Dini altyazı sadece yararlı cevapların gönderilmesini önler - lütfen soruları perspektif içinde tutun.


95

İşte çözüm:

<?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]);
?>

Derin test etmek için, dize otomatik kapanış etiketlerine aşağıdaki gibi girdim:

  1. <sa />
  2. <br/>
  3. <br>

Ayrıca etiketleri de girdim:

  1. bir özellik
  2. birden fazla özellik
  3. değeri tek tırnak veya çift ​​tırnak içine alan özellikler
  4. sınırlayıcı bir çift tırnak işareti olduğunda tek tırnak işaretleri içeren özellikler ve bunun tersi
  5. "unpretty", "=" simgesinden önce, sonra ve hem önce hem de sonra boşluk bırakıyor.

Yukarıdaki kavramın kanıtında işe yaramayan bir şey bulursanız, becerilerimi geliştirmek için kodu analiz etmede hazırım.

<EDIT> Kullanıcıdan gelen sorunun kendi kendine kapanan etiketlerin ayrıştırılmasını önlemek olduğunu unuttum. Bu durumda, desen daha basittir ve buna dönüşür:

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

User @ridgerunner, kalıbın tırnaksız özniteliklere veya değersiz özniteliklere izin vermediğini fark etti . Bu durumda, ince bir ayar bize aşağıdaki deseni getirir:

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

</ DÜZENLEME>

Deseni anlama

Birisi desen hakkında daha fazla bilgi edinmekle ilgileniyorsa, bazı çizgiler sağlarım:

  1. ilk alt ifade (\ w +) etiket adıyla eşleşir
  2. ikinci alt ifade bir özniteliğin desenini içerir. Oluşumu:
    1. bir veya daha fazla boşluk \ s +
    2. özelliğin adı (\ w +)
    3. sıfır veya daha fazla boşluk \ s * (buradaki boşluklar bırakılabilir)
    4. "=" sembolü
    5. yine, sıfır veya daha fazla boşluk
    6. öznitelik değerinin sınırlayıcısı, tek veya çift tırnak işareti ('| "). Desende, PHP tırnak ayracı ile çakıştığı için tek tırnaktan çıkar. özelliğin kapanışını ayrıştırmak için, bu yüzden çok önemlidir.
    7. Neredeyse her şeyle eşleşen özelliğin değeri : (. *?); açgözlü eşleşmeyi kullanarak bu özel sözdiziminde (yıldız işaretinden sonraki soru işareti) kullanarak RegExp motoru, bu alt ifadeyi izleyenlerden başka bir şeyle eşleşen "ileriye dönük" gibi bir işleç
    8. eğlence geliyor: \ 4 kısmı bir backreference operatörü desende daha önce tanımlanan bir alt ifadeye atıfta , bu durumda, bulunan ilk özellik sınırlayıcı olan dördüncü alt ifadeye atıfta bulunuyorum
    9. sıfır veya daha fazla boşluk \ s *
    10. öznitelik alt ifadesi burada yıldız işareti tarafından verilen sıfır veya daha fazla olası oluşumun belirtimi ile sona erer.
  3. Ardından, bir etiket ">" simgesinden önce boşlukla bitebileceğinden, sıfır veya daha fazla boşluk boşluğu \ s * alt şablonuyla eşleştirilir.
  4. Eşleşecek etiket, basit bir ">" simgesiyle veya eğik çizgiden önce yararlanan olası bir XHTML kapatma ile bitebilir: (/> |>). Kesik çizgi, normal ifade sınırlayıcısına denk geldiği için kaçar.

Küçük ipucu: Bu kodu daha iyi analiz etmek için kaçan HTML özel karakterleri sağlamadığım için üretilen kaynak koduna bakmak gerekir.


12
Değeri olmayan niteliklere sahip geçerli etiketlerle eşleşmez, yani <option selected>. Ayrıca, geçerli etiketleri sıralanmamış özellik değerleriyle eşleştirmez, yani <p id=10>.
ridgerunner

1
@ridgerunner: Yorumunuz için çok teşekkürler. Bu durumda, desen biraz değişmelidir: $ pattern = '/ <(\ w +) (\ s + (\ w +) (\ s * \ = \ s * (\' | "|) (. *?) \\ 5 \ s *)?) * \ S *> / '; Test ettim ve alıntılanmamış özellikler veya değeri olmayan özellikler durumunda çalışır
Emanuele Del Grande

Etiket adından önceki bir alana ne dersiniz: < a href="http://wtf.org" >Yasal olduğundan eminim, ancak eşleşmiyorsunuz.
Floris

7
HAYIR üzgünüm, bir tagname öncesi boşluklar yasadışı. "Oldukça emin" olmanın ötesinde neden itirazınıza dair bazı kanıtlar sunmuyorsunuz? XML 1.1'e atıfta bulunulan benim, w3.org/TR/xml11/#sec-starttags ve HTML 4, 5 ve XHTML için aynısını bulabilirsiniz, çünkü bir test yaparsanız W3C doğrulaması da uyarır. Buradaki diğer birçok blah-blah-şair olarak, kodumun soruda belirtilen sözleşme kurallarına göre nerede başarısız olduğunu göstermek için, cevaplarımdan yüzlerce eksi dışında hala akıllı bir argüman almadım . Onları sadece memnuniyetle karşılarım.
Emanuele Del Grande

@ridgerunner elbette yorumunuz akıllı ve hoş geldiniz.
Emanuele Del Grande

91

Bir HTML belgesinden bir şeyi hızlı bir şekilde çıkarmam gerektiğinde, XML'e dönüştürmek için Tidy'i kullandım ve sonra ihtiyacım olanı almak için XPath veya XSLT'yi kullanıyorum. Senin durumunda, böyle bir şey:

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

89

Daha önce HTMLParser adlı açık kaynaklı bir araç kullandım . HTML'yi çeşitli şekillerde ayrıştırmak için tasarlanmıştır ve amaca oldukça iyi hizmet eder. HTML'yi farklı treenode olarak ayrıştırabilir ve öznitelikleri düğümden çıkarmak için API'sını kolayca kullanabilirsiniz. Göz atın ve size yardımcı olup olamayacağını görün.


84

HTML'yi normal ifadelerle ayrıştırmayı seviyorum. Kasten kırılmış salak HTML'yi ayrıştırmaya çalışmıyorum. Bu kod benim ana ayrıştırıcı (Perl sürümü):

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

Buna htmlsplit denir, HTML'yi satırlara böler, her satırda bir etiket veya metin parçası bulunur. Sonra satırları grep , sed , Perl, vb gibi diğer metin araçları ve komut dosyaları ile daha fazla işlenebilir . Ben bile şaka değilim :) Keyfini çıkarın.

Çok büyük web sayfalarını işlemek istiyorsanız, slurp-everything-first Perl betiğimi güzel bir akış şeye dönüştürmek yeterince basit. Ama gerçekten gerekli değil.

Bahse girerim bunun için indirileceğim.

HTML Bölünmesi


Beklentilerime karşı bu bazı artışlar aldı, bu yüzden daha düzenli ifadeler önereceğim:

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

XML / XHTML için iyidirler.

Küçük varyasyonlarla, dağınık HTML ile başa çıkabilir ... veya önce HTML -> XHTML'yi dönüştürebilir.


Düzenli ifadeler yazmanın en iyi yolu , opak tek katmanlar veya yorumlanmış çok satırlı canavarlıklar olarak değil Lex / Yacc tarzındadır. Bunu henüz burada yapmadım; bunların zar zor ihtiyacı var.


35
"Kasten kırılmış salak HTML'yi ayrıştırmaya çalışmıyorum." Kodunuz farkı nasıl biliyor?
Kevin Panko

HTML'nin bozuk olup olmadığı pek önemli değil. Bu şey yine de HTML'yi etiketlere ve metne böler. Rahatsız edebilecek tek şey, insanların metin veya niteliklere kaçış <veya> karakterleri eklemesidir. Pratikte, küçük HTML ayırıcım iyi çalışıyor. Buluşsal yöntemlerle dolu devasa bir canavarlık takozuna ihtiyacım yok. Basit çözümler herkes için değildir ...!
Sam Watkins

XML / XHTML için etiketleri, metni ve öznitelikleri ayıklamak için bazı basit ifadeler ekledim.
Sam Watkins

(get bug 1 hataları) /(\w+)="(.*?)"/çift ​​tırnak olduğunu varsayar. Değerleri tek tırnak içinde özleyecektir. Html sürüm 4 ve önceki sürümlerde, basit bir sözcükse, tırnaksız değere izin verilir.
David Andersson

(get attributes bug 2) /(\w+)="(.*?)"/bir özellik içindeki bir niteliğe benzeyen metinle yanlış eşleşebilir, örn <img title="Nope down='up' for aussies" src="..." />. Küresel olarak uygulanırsa, sıradan metin veya html yorumlarında bu tür şeylerle de eşleşir.
David Andersson

74

İşte bazı ungodly regex kullanarak HTML ayrıştıran bir PHP tabanlı ayrıştırıcı . Bu projenin yazarı olarak, HTML'yi regex ile ayrıştırmanın mümkün olduğunu söyleyebilirim, ancak verimli değil. Bir sunucu tarafı çözümüne ihtiyacınız varsa ( wp-Tipografi WordPress eklentim için yaptığım gibi ), bu işe yarar.


1
htmlawed , HTML'yi filtrelemek, dönüştürmek, vb. için ayrıştıran başka bir PHP projesidir.
user594694

Hayır olamaz regex ile ayrıştırma HTML. Ancak bazı alt kümeleri için, bu olabilir çalışır.
mirabilos

71

Burada HTML'yi BBCode ile değiştirmek için bazı güzel ifadeler var . Tüm söyleyenler için, HTML'yi tam olarak ayrıştırmaya çalışmadığını, sadece sterilize etmek için olduğunu unutmayın. Muhtemelen basit "ayrıştırıcısının" anlayamadığı etiketleri öldürmeyi göze alabilir.

Örneğin:

$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
Bunu yapma. Lütfen.
maletor

68

(X) HTML'yi ayrıştırmak için RegExp yöntemleri sorusu hakkında, bazı sınırlardan bahsedenlerin cevabı şudur: NOBODY burada özyineleme hakkında konuştuğundan , bu güçlü silahın gücünü yönetecek kadar eğitilmemişsiniz .

Bir RegExp-agnostik meslektaşı bana bu tartışmayı bildirdi, bu web'de bu eski ve sıcak konuyla ilgili kesinlikle ilk değil.

Bazı mesajları okuduktan sonra, yaptığım ilk şey bu iş parçacığında "? R" dizesini aramaktı. İkincisi, "özyineleme" hakkında arama yapmaktı.
Hayır, kutsal inek, eşleşme bulunamadı.
Bir ayrıştırıcının üzerine inşa edildiği ana mekanizmadan kimse bahsetmediği için, yakında kimsenin bu konuya gelmediğinin farkındaydım.

(X) HTML ayrıştırıcısının özyinelemeye ihtiyacı varsa, özyineleme içermeyen bir RegExp ayrıştırıcısı bu amaç için yeterli değildir. Basit bir yapı.

RegExp siyah sanatını güç olduğunu , bu yüzden belki çalışıyor ve bir elinde tüm web yakalamak için kişisel çözüm test ederken biz dışarı sol seçenek daha vardır ... Eh, bu konuda eminim :)

İşte sihirli desen:

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

Sadece dene.
Bir PHP dizesi olarak yazılmıştır, bu nedenle "s" değiştirici sınıfların satırsonu içermesini sağlar.
İşte Ocak ayında yazdığım PHP el kitabı hakkında bir örnek not : Reference

(Bu notta "m" değiştiricisini yanlış kullandığım konusunda dikkatli olun; hiçbir ^ veya $ ankraj kullanılmadığından, RegExp motoru tarafından atılmasına rağmen silinmelidir).

Şimdi, bu yöntemin sınırları hakkında daha bilinçli bir bakış açısıyla konuşabiliriz:

  1. RegExp motorunun özel uygulamasına göre, özyinelemenin ayrıştırılan iç içe şablonların sayısında bir sınır olabilir , ancak kullanılan dile bağlıdır
  2. bozuk (x) HTML ciddi hatalara yol açmasa da sterilize edilmez .

Her neyse, bu sadece bir RegExp kalıbıdır, ancak birçok güçlü uygulama geliştirme olasılığını açıklar. Çerçevemde oluşturduğum bir şablon motorunun özyinelemeli iniş ayrıştırıcısına
güç vermek için bu deseni yazdım ve hem yürütme sürelerinde hem de bellek kullanımında (aynı sözdizimini kullanan diğer şablon motorlarıyla ilgisi yok) performanslar gerçekten harika.


35
Bunu "özniteliklerden daha büyük izin vermeyen Regex" kutusuna koyacağım. <İnput value = "5> 3 mü?" />
Gareth

68
Eğer üretim koduna böyle bir şey koyarsanız, muhtemelen bakıcı tarafından vurulursunuz. Bir jüri onu asla mahkum etmezdi.
aehiilrs

30
Düzenli ifadeler işe yaramaz çünkü tanım gereği özyinelemezler. Normal ifadelere özyinelemeli bir işleç eklemek, temel olarak yalnızca daha zayıf sözdizimine sahip bir CFG yapar. Neden gereksiz işlevsellik ile taşan bir şeye özyinelemeyi şiddetle eklemek yerine ilk etapta özyinelemeli olarak tasarlanmış bir şey kullanmıyorsunuz?
Welbog

16
İtirazım, yatırım yapılan zamanlardan biri olan bir işlevsellik değil. RegEx ile ilgili sorun, cutsey küçük bir gömlek sonrası zaman daha verimli bir şey yaptığını görünmektedir ("Bir kod satırı bakın!"). Ve elbette hiç kimse hile sayfalarıyla geçirdikleri yarım saatten (veya 3) ve (umarım) olası her giriş permütasyonunu test etmekten bahsetmez. Ve tüm bunları geçtikten sonra, bakıcı kodu anlamaya veya doğrulamaya gittiğinde, sadece ona bakamaz ve doğru olduğunu göremezler. İfadeyi incelemek ve aslında tekrar tekrar test etmek zorunda ...
Oorang

15
... iyi olduğunu bilmek için. Ve bu regex ile iyi olan insanlarda bile olacak . Ve dürüst olmak gerekirse, insanların ezici çoğunluğunun bunu iyi bilmeyeceğinden şüpheleniyorum. Bu yüzden en kötü bakım kabuslarından birini alıp diğer bakım kabusu olan özyineleme ile birleştiriyorsunuz ve bence projemde gerçekten ihtiyacım olan şey biraz daha az akıllı biri. Amaç, kötü programcıların kod tabanını bozmadan koruyabilecekleri kod yazmaktır. En az ortak paydaya kod yazmanın çok güzel olduğunu biliyorum. Ama mükemmel yetenekleri işe almak zordur ve sık sık ...
Oorang

62

Birçok kişinin belirttiği gibi, HTML normal bir dil değildir ve ayrıştırmayı çok zorlaştırabilir. Buna benim çözümüm düzenli bir program kullanarak normal bir dile dönüştürmek ve daha sonra sonuçları tüketmek için bir XML ayrıştırıcı kullanmaktır. Bunun için birçok iyi seçenek var. Programım, HTML'yi XML'e dönüştürmek için jtidy kütüphanesi ile Java ve ardından sonuca Jaxen için xxath kullanılarak yazılmıştır .


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

Açıklanan kısımlar:

<: başlangıç ​​karakteri

\s*: etiket adından önce boşluklara sahip olabilir (çirkin fakat mümkün).

(\w+): etiketler harf ve rakam içerebilir (h1). Eh, \w'_' ile de eşleşir, ama sanırım acıtmaz. Meraklıysa ([a-zA-Z0-9] +) kullanın.

[^/>]*: kapanış hariç >ve /kapanana kadar her şey>

>: kapanış >

İLGİSİZ

Ve sadece normal diller kadar güçlü olduklarını söyleyerek düzenli ifadeleri hafife alan arkadaşlara:

a n ba n ba n , düzenli olmayan ve hatta bağlamdan bağımsız olmayan ile eşleştirilebilir^(a+)b\1b\1$

Backreferencing FTW !


@GlitchMr, onun amacı buydu. Modern düzenli ifadeler teknik olarak düzenli değildir ve bunun için herhangi bir neden yoktur.
alanaktion

3
@alanaktion: "Modern" normal ifadeler (okuma: Perl uzantılı), O(MN)(M normal ifade uzunluğu, N metin uzunluğu) ile eşleşemez . Geri bildirimler bunun nedenlerinden biridir. Awk uygulamasında geri başvurular yoktur ve O(MN)zaman içindeki her şeyle eşleşir .
Konrad Borowski

56

Bu etiketleri bulmaya çalışıyorsanız (ayrıştırma hırsları olmadan) şu normal ifadeyi deneyin:

/<[^/]*?>/g

30 saniye içinde yazdım ve burada test ettim: http://gskinner.com/RegExr/

Yoksaymak istediğinizi söylediğiniz türleri yok sayarken, belirttiğiniz etiket türleriyle eşleşir.


2
Sanırım bunun \/>yerine demek istiyorsun \\>.
Justin Morgan

Hayır, \>demek istediğim sadece ; Asla orijinal yazımın normal ifadesini düzenlemek istemedim.
Lonnie Best

2
Bilginize, açılı parantezlerden kaçmanıza gerek yok. Tabii ki, yine de onlardan kaçmanın hiçbir zararı yoktur, ancak kaçınabileceğiniz karışıklığa bakın. ;)
Alan Moore

Bazen bir şeyin özel bir karakter olup olmadığından emin olmadığımda gereksiz yere kaçarım. Cevabı düzenledim; aynı ama daha özlü çalışır.
Lonnie Best

Şimdi buna baktığımda, neden istediğini düşündüğümü bilmiyorum \/, çünkü bu şartların tam tersini yapıyor. Belki negatif bir filtre modeli sunduğunuzu sanıyordum.
Justin Morgan

54

Bana öyle geliyor ki sonunda "/" olmadan etiketleri eşleştirmeye çalışıyorsunuz. Bunu dene:

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

8
Bu çalışmıyor. '<Xa = "<b>" /> <y>' girişi için, x sona erdirilse de eşleşmeler x ve y'dir.
ceving

51

Programlama sırasında, HTML ile uğraşırken, özellikle doğruluk çok önemliyse (örneğin, işleminizin güvenlikle ilgili sonuçları varsa), normal ifadeler yerine özel ayrıştırıcılar ve API'lar kullanmanın en iyisi olduğu doğrudur. Ancak, XML tarzı işaretlemenin asla normal ifadelerle işlenmemesi gerektiği dogmatik bir görüşe atlamıyorum. Normal ifadelerin iş için harika bir araç olduğu, örneğin bir metin düzenleyicide bir kerelik düzenlemeler yaparken, bozuk XML dosyalarını düzeltirken veya gibi görünen ancak tam olarak XML olmayan dosya biçimleriyle uğraşmak gibi durumlar vardır. Dikkat edilmesi gereken bazı sorunlar vardır, ancak bunlar aşılmaz ve hatta ille de alakalı değildir.

Az <([^>"']|"[^"]*"|'[^']*')*>önce bahsettiğim gibi basit bir normal ifade genellikle yeterince iyidir. Bu, her şey göz önüne alındığında naif bir çözümdür, ancak >özellik değerlerinde kodlanmamış sembollere doğru bir şekilde izin verir . Örneğin, bir tableetiket arıyorsanız, etiketi olarak uyarlayabilirsiniz </?table\b([^>"']|"[^"]*"|'[^']*')*>.

Daha "gelişmiş" bir HTML regex'in nasıl görüneceğine dair bir fikir vermek için, aşağıdakiler gerçek dünya tarayıcı davranışını ve HTML5 ayrıştırma algoritmasını taklit etmek için oldukça saygın bir iş yapar:

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

Aşağıdakiler, XML etiketlerinin oldukça katı bir tanımıyla eşleşir (XML adlarında izin verilen tüm Unicode karakter kümesini hesaba katmasa da):

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

Kabul edilirse, bunlar çevredeki bağlamı ve birkaç uç durumu açıklamaz, ancak gerçekten isteseniz bile bu tür şeyler ele alınabilir (örneğin, başka bir normal ifadenin maçları arasında arama yaparak).

Günün sonunda, bu aracın normal ifade olduğu durumlarda bile iş için en uygun aracı kullanın.


49

Düzenli ifadeleri bu amaç için kullanmak uygun ve etkili olmasa da, bazen düzenli ifadeler basit eşleme sorunlarına hızlı çözümler sağlar ve bence önemsiz işler için düzenli ifadeler kullanmak o kadar korkunç değildir.

Bir yoktur kesin blog yazısı Steven Levithan tarafından yazılmış en içteki HTML öğelerini eşleştirme hakkında.

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.