2013-08-01 19 views
5

RegEx match open tags except XHTML self-contained tags ile bağlantı kurmaya başlamadan önce tüm soruları okuyun.HTML ayrıştırıcısı nasıl oluşturulur?

Kendimi yeni bir şeyler öğrenmek için bir HTML ayrıştırıcısı yazmak (yalnızca HTML 5 için HTML 5 olup olmadığını kontrol etmeli, yoksa bir hata döndürmeli) istiyorum ama ne olduğunu bilmiyorum Bunu yapmanın en iyi yolu.

<!doctype html> 
<html> 
<head> 
    <!-- #TITLE --> 
    <title>Just an example</title> 
</head> 
<body> 
    <p class='main'>Simple paragraph with an <a href='/a.html'>anchor</a></p> 
</body> 
</html> 

Artık herkes nasıl bu (önemli değil nihai şeklini, sadece bir kavram) ayrıştırmak için beni gösterilebilir: Size bir örnek göstereyim? Bazı fikirlerim vardı (özyinelemeli işlevler kullanmak ya da gerçek etiketi barındıran diziye başvurmak gibi), ama bunların en iyi kavramlar olduğunu sanmıyorum. Char'i char ile kontrol edip belirli işlevleri çağırmalı mıyım yoksa normal ifadeler kullanmalı mıyım (aşağıda açıklanmıştır)?

Düzenli ifadeler kullanarak etiketin tamamı için tek bir kalıp anlamına gelmez. Daha çok tagname için bir kalıp kullanıyorum (ve eğer bu doğru gelirse, sonraki kalıpları kontrol edin), daha sonra nitelik için (ve eğer bu doğruysa, tekrar kontrol edin) ve son olarak etiketin sonunu kontrol etmeyi kastediyorum.

Etiket bulduğumda ne yapmalıyım? Etiketleri kontrol eden bir döngü çalıştırın (ve etiket bulursa, tekrar ve tekrar arayın ...)? Ama benim için X, X fonksiyonunu çağırdığında, yinelemeli fonksiyon ya da en azından yarım-yinelemeli gibi görünüyor X ...

Son soru şudur: Bunun için en verimli ve doğru yapı nedir?

+2

Cevabınızın bana yardım ettiğini sanmıyorum ...Bu soruyu daha önce görmüştüm ve “** Normal ifadeleri kullanarak tüm etiket için bir kalıp demek istemiyorum. **” Ve bu arada, bunu 2 dakikadan kısa bir sürede nasıl okudunuz? Bir lexer'dan bahsetmek için – user1951214

cevap

2

SGML tabanlı bir ayrıştırıcı yazmanın en büyük kısmı lexer'dır. İşte özel bir lexer oluşturma hakkında bir makale: http://onoffswitch.net/building-a-custom-lexer/.

Benim düşünceme göre, normal ifadeler muhtemelen overkill/uygunsuz olur - HTML tokenleri ile eşleşmek istersiniz ve karakter ayrıştırmasıyla karakter yapmak muhtemelen bunu yapmanın en iyi yoludur.

+1

+1. OP'nin gerçekten bakması gereken şey budur. Bir lexer gerekliliği hakkında daha fazla puan için, @ JXG'nin bu konudaki yorumu öğreticidir: http://stackoverflow.com/questions/2400623/how-do-html-parses-work-if-theyre-not-using-regexp ? rq = 1 –

+1

Merhaba, bu blog yazısının yazarıyım, sadece dilbilgisini biliyorsanız ANTLR gibi bir ayrıştırıcı jeneratörü kullanarak daha hızlı çalışıp daha hızlı koşabileceğinizi belirtmek istedim. Tüm lexer'ı sıfırdan inşa etmek istediğin şeye bağlı olarak aşırı olabilir. Ayrıca, bir belirteç aşamasından geçmek zorunda kalmadan AST almak için ayrıştırıcı birleştirici ve birleştirici kitaplıklarına bakmaya değecektir. – devshorts

3

@ Kian'ın cevabı bir lexer kullanarak bahseder, fakat algoritmalar açısından, özyinelemeyi kullanmak isteyeceğinizi düşünüyorum. HTML bütün bir özyinelemeli yapısına peşinde: İşte

<div> 
    <div> 
     <div> 
     </div> 
    </div> 
</div> 

saf JS örnektir - bu tam bir uygulama olmasa da. (Ben <empty /> elemanlar için hiç desteği ekledik; için <!-- comments -->; &entities; için; xmlns:namespaces için ... tam teşekküllü HTML veya XML ayrıştırıcı yazma büyük bir girişim, bu yüzden hafife almayın) özellikle

Bu çözüm sözcüksel analiz sürecini atlar, ama kasten cevapımı Kian'larla karşılaştırmak için kast ettim.

var markup = "<!DOCTYPE html>\n"+ 
      "<html>\n"+ 
      " <head>\n"+ 
      " <title>Example Input Markup</title>\n"+ 
      " </head>\n"+ 
      " <body>\n"+ 
      " <p id=\"msg\">\n"+ 
      "  Hello World!\n"+ 
      " </p>\n"+ 
      " </body>\n"+ 
      "</html>"; 

parseHtmlDocument(markup); 

// Function definitions 

function parseHtmlDocument(markup) { 
    console.log("BEGIN DOCUMENT"); 
    markup = parseDoctypeDeclaration(markup); 
    markup = parseElement(markup); 
    console.log("END DOCUMENT"); 
} 

function parseDoctypeDeclaration(markup) { 
    var regEx = /^(\<!DOCTYPE .*\>\s*)/i; 
    console.log("DOCTYPE DECLARATION"); 
    var matches = regEx.exec(markup); 
    var doctypeDeclaration = matches[1]; 
    markup = markup.substring(doctypeDeclaration.length); 
    return markup; 
} 

function parseElement(markup) { 
    var regEx = /^\<(\w*)/i; 
    var matches = regEx.exec(markup); 
    var tagName = matches[1]; 
    console.log("BEGIN ELEMENT: "+tagName); 
    markup = markup.substring(matches[0].length); 
    markup = parseAttributeList(markup); 
    regEx = /^\>/i; 
    matches = regEx.exec(markup); 
    markup = markup.substring(matches[0].length); 
    markup = parseNodeList(markup); 
    regEx = new RegExp("^\<\/"+tagName+"\>"); 
    matches = regEx.exec(markup); 
    markup = markup.substring(matches[0].length); 
    console.log("END ELEMENT: "+tagName); 
    return markup; 
} 

function parseAttributeList(markup) { 
    var regEx = /^\s+(\w+)\=\"([^\"]*)\"/i; 
    var matches; 
    while(matches = regEx.exec(markup)) { 
     var attrName = matches[1]; 
     var attrValue = matches[2]; 
     console.log("ATTRIBUTE: "+attrName); 
     markup = markup.substring(matches[0].length); 
    } 
    return markup; 
} 

function parseNodeList(markup) { 
    while(markup) { 
     markup = parseTextNode(markup); 
     var regEx = /^\<(.)/i; 
     var matches = regEx.exec(markup); 
     if(matches[1] !== '/') { 

      markup = parseElement(markup); 
     } 
     else { 
      return markup; 
     } 
    } 
} 

function parseTextNode(markup) { 
    var regEx = /([^\<]*)\</i; 
    var matches = regEx.exec(markup); 
    markup = markup.substring(matches[1].length); 
    return markup; 
} 

İdeal bu fonksiyonların her XML specification tanımlanan gramer üzerine çok yakından harita olacaktır. yani ideal biz parseElement() fonksiyonu Bunun gibi daha bakmak isterdim ...

element ::= EmptyElemTag | STag content ETag 

: Örneğin, şartname bir element şöyle tanımlar

function parseElement(markup) { 
    if(nextTokenIsEmptyElemTag) { // this kind of logic is where a lexer will help! 
     parseEmptyElemTag(markup); 
    } 
    else { 
     parseSTag(markup); 
     parseContent(markup); 
     parseETag(markup); 
    } 
} 

... ama ettik Örneğimi yazarak bazı köşeleri kesiyorum, bu yüzden gerçek dilbilgisini gerektiği kadar yansıtmıyor.

İlgili konular