Überprüfen Sie, ob eine Zeichenfolge HTML ist oder nicht

98

Ich habe eine bestimmte Zeichenfolge, für die ich überprüfen möchte, ob es sich um eine HTML-Datei handelt oder nicht. Ich verwende Regex für das gleiche, aber nicht das richtige Ergebnis.

Ich habe meine Regex validiert und es funktioniert hier gut .

var htmlRegex = new RegExp("<([A-Za-z][A-Za-z0-9]*)\b[^>]*>(.*?)</\1>");
return htmlRegex.test(testString);

Hier ist die Geige, aber der Regex läuft dort nicht hinein. http://jsfiddle.net/wFWtc/

Auf meinem Computer läuft der Code einwandfrei, aber als Ergebnis wird falsch statt wahr angezeigt. Was fehlt hier?

user1240679
quelle
5
Verwenden Sie einen HTML-Parser, um HTML zu analysieren. Bitte lesen Sie dies, wenn Sie es noch nicht getan haben.
Frédéric Hamidi
3
Die Frage kommt immer wieder, es sollte einen Stack-Bot geben, der aoutmatisch einen Kommentar zu jeder Frage mit HTML und Regex enthält
Bartlomiej Lewandowski
2
Es hängt irgendwie davon ab, welchen Grad an Raffinesse Sie von der Prüfung erwarten. Sie können überprüfen, ob die Zeichenfolge mindestens eine <und mindestens eine enthält, >und sie als HTML bezeichnen, oder Sie können überprüfen, ob sie mit der richtigen HTML-Syntax oder irgendetwas dazwischen streng gültig ist. Im einfachsten Fall ist ein HTML-Parser nicht erforderlich.
JJJ
2
Warum überprüfen Sie, ob eine Zeichenfolge HTML ist?
nhahtdh
2
@ user1240679: Gültiges Markup-Format? Welche Gültigkeit? Im strengsten Sinne benötigen Sie DTD, um es zu beschreiben. In einem losen Sinne möchten Sie vielleicht überprüfen, ob die Tags richtig übereinstimmen. Jeder der beiden oben genannten Fälle ist kein Job für Regex.
nhahtdh

Antworten:

314

Ein besserer Regex, um zu überprüfen, ob eine Zeichenfolge HTML ist, ist:

/^/

Beispielsweise:

/^/.test('') // true
/^/.test('foo bar baz') //true
/^/.test('<p>fizz buzz</p>') //true

Tatsächlich ist es so gut, dass es truefür jede übergebene Zeichenfolge zurückgegeben wird, da jede Zeichenfolge HTML ist . Im Ernst, auch wenn es schlecht formatiert oder ungültig ist, ist es immer noch HTML.

Wenn Sie nach HTML-Elementen suchen und nicht nur nach Textinhalten, können Sie Folgendes verwenden:

/<\/?[a-z][\s\S]*>/i.test()

Es hilft Ihnen nicht, den HTML-Code in irgendeiner Weise zu analysieren, aber es kennzeichnet die Zeichenfolge mit Sicherheit als HTML-Elemente enthaltend.

zzzzBov
quelle
46
Ich bin ehrlich überrascht, dass ich keine weiteren Downvotes für den Snark bekommen habe.
zzzzBov
7
@clenemt, also denkst du a < b && a > c, HTML zu sein?
zzzzBov
1
@zzzzBov Sie wissen, dass Sie a<b && a>cHTML betrachten ... Ich wünschte, die HTML-Erkennung könnte so stark vereinfacht werden. Das Parsen ist nie einfach.
Oriadam
2
@oriadam, der Kontext war für die Erkennung von Elementen in diesem Fall. Wenn Sie a < b && a > cden Browser die drehen >und <Zeichen in &gt;und &lt;entsprechend Einheiten. Wenn Sie stattdessen a<b && a>cden Browser verwenden, wird das Markup so interpretiert, a<b && a>c</b>dass das Fehlen eines Leerzeichens bedeutet, dass <bein <b>Element geöffnet wird . Hier ist eine kurze Demo von dem, worüber ich spreche .
zzzzBov
4
Dies ist wahrscheinlich die Trollantwort mit der höchsten Stimme, die ich je gesehen habe. ;)
aandis
72

Methode 1 . Hier ist die einfache Funktion zum Testen, ob die Zeichenfolge HTML-Daten enthält:

function isHTML(str) {
  var a = document.createElement('div');
  a.innerHTML = str;

  for (var c = a.childNodes, i = c.length; i--; ) {
    if (c[i].nodeType == 1) return true; 
  }

  return false;
}

Die Idee ist, dem Browser-DOM-Parser zu ermöglichen, zu entscheiden, ob die angegebene Zeichenfolge wie HTML aussieht oder nicht. Wie Sie sehen können, wird einfach nach ELEMENT_NODE( nodeTypevon 1) gesucht .

Ich habe ein paar Tests gemacht und es sieht so aus, als ob es funktioniert:

isHTML('<a>this is a string</a>') // true
isHTML('this is a string')        // false
isHTML('this is a <b>string</b>') // true

Diese Lösung erkennt HTML-Zeichenfolgen ordnungsgemäß, hat jedoch den Nebeneffekt, dass img / vide / etc. Tags beginnen mit dem Herunterladen der Ressource, sobald sie in innerHTML analysiert wurden.

Methode 2 . Eine andere Methode verwendet DOMParser und hat keine Nebenwirkungen beim Laden von Ressourcen:

function isHTML(str) {
  var doc = new DOMParser().parseFromString(str, "text/html");
  return Array.from(doc.body.childNodes).some(node => node.nodeType === 1);
}

Hinweise:
1. Array.fromist die ES2015-Methode, kann durch ersetzt werden [].slice.call(doc.body.childNodes).
2. Die Pfeilfunktion im someAnruf kann durch die übliche anonyme Funktion ersetzt werden.

dfsq
quelle
3
Das ist eine großartige Idee. Diese Funktion konnte jedoch kein schließendes Tag (dh isHTML("</a>") --> false) erkennen.
Lewis
9
Tolle Lösung! .. Der einzige negative Nebeneffekt ist, dass wenn Ihr HTML statische Ressourcen wie ein Bild-src-Attribut enthält .. innerHTMLder Browser gezwungen wird, diese Ressourcen abzurufen. :(
Jose Browne
@JoseBrowne, auch wenn es nicht an das DOM angehängt ist?
Kuus
1
@kuus Ja, auch wenn nicht angehängt. Verwenden Sie die DOMParser-Lösung.
dfsq
1
Gute Idee, aber wäre die akzeptierte Antwort nicht besser für die Leistung? Vor allem, wenn Sie große Saiten haben (Wortspiel beabsichtigt) oder wenn Sie diesen Test häufig verwenden müssen.
DerpyNerd
13

Ein bisschen Validierung mit:

/<(?=.*? .*?\/ ?>|br|hr|input|!--|wbr)[a-z]+.*?>|<([a-z]+).*?<\/\1>/i.test(htmlStringHere) 

Dies sucht nach leeren Tags (einige vordefiniert) und /beendet leere XHTML-Tags und wird aufgrund des leeren Tags als HTML validiert. ODER erfasst den Tag-Namen und versucht, das schließende Tag irgendwo in der Zeichenfolge zu finden, um es als HTML zu validieren.

Erklärte Demo: http://regex101.com/r/cX0eP2

Aktualisieren:

Vollständige Validierung mit:

/<(br|basefont|hr|input|source|frame|param|area|meta|!--|col|link|option|base|img|wbr|!DOCTYPE).*?>|<(a|abbr|acronym|address|applet|article|aside|audio|b|bdi|bdo|big|blockquote|body|button|canvas|caption|center|cite|code|colgroup|command|datalist|dd|del|details|dfn|dialog|dir|div|dl|dt|em|embed|fieldset|figcaption|figure|font|footer|form|frameset|head|header|hgroup|h1|h2|h3|h4|h5|h6|html|i|iframe|ins|kbd|keygen|label|legend|li|map|mark|menu|meter|nav|noframes|noscript|object|ol|optgroup|output|p|pre|progress|q|rp|rt|ruby|s|samp|script|section|select|small|span|strike|strong|style|sub|summary|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|track|tt|u|ul|var|video).*?<\/\2>/i.test(htmlStringHere) 

Dies führt eine ordnungsgemäße Validierung durch, da es ALLE HTML-Tags enthält , leere zuerst, gefolgt von den übrigen, die ein schließendes Tag benötigen.

Erklärte Demo hier: http://regex101.com/r/pE1mT5

CSᵠ
quelle
1
Nur eine Anmerkung, der untere reguläre Ausdruck funktioniert zwar, erkennt jedoch keine nicht geschlossenen HTML-Tags wie "'<strong> Hallo Welt". Zugegeben, dies ist fehlerhaft. HTML sollte daher als Zeichenfolge behandelt werden. Aus praktischen Gründen möchte Ihre App diese möglicherweise auch erkennen.
TK123
HTML wurde unter Berücksichtigung der Vergebung von Benutzeragenten entwickelt. "Ungültige" Tags sind nicht ungültig, sie sind nur unbekannt und zulässig. "Ungültige" Attribute sind nicht ungültig ... Dies ist besonders bemerkenswert, wenn man anfängt, "Webkomponenten" und Technologien wie JSX einzubeziehen, die HTML und umfangreichere Komponentenbeschreibungen mischen und typischerweise Schatten-DOM erzeugen. Schlagen Sie dies in eine Datei und bewerten Sie document.querySelector('strange')- es wird funktionieren.
Amcgregor
(Zusammenfassend: Aufgrund der Art und Weise, wie die Spezifikation geschrieben ist, ist der Versuch, HTML-Markups zu "validieren", im Wesentlichen ein Kinderspiel. Der Link zu einem Beispiel-HTML-Dokument mit einem "ungültigen" Element ist zu 100% vollständig. vollständiges HTML-Dokument - und seit 1997 - als weiteres Beispiel.)
amcgregor
9

Die obige Antwort von zzzzBov ist gut, berücksichtigt jedoch keine streunenden schließenden Tags, wie zum Beispiel:

/<[a-z][\s\S]*>/i.test('foo </b> bar'); // false

Eine Version, die auch schließende Tags abfängt, könnte folgende sein:

/<[a-z/][\s\S]*>/i.test('foo </b> bar'); // true
AeonOfTime
quelle
Hätte besser sein können, eine Bearbeitung vorzuschlagen, als dies als Kommentar zu posten.
Zlatin Zlatev
Ich denke du meinst <[a-z/][\s\S]*>- beachte den Schrägstrich in der ersten Gruppe.
Ryan Guill
7

Hier ist ein schlampiger Einzeiler, den ich von Zeit zu Zeit benutze:

var isHTML = RegExp.prototype.test.bind(/(<([^>]+)>)/i);

Grundsätzlich wird truefür Zeichenfolgen zurückgegeben, die ein <gefolgt von ANYTHINGgefolgt von gefolgt sind >.

Damit ANYTHINGmeine ich im Grunde alles außer einer leeren Zeichenfolge.

Es ist nicht großartig, aber es ist ein Einzeiler.

Verwendung

isHTML('Testing');               // false
isHTML('<p>Testing</p>');        // true
isHTML('<img src="hello.jpg">'); // true
isHTML('My < weird > string');   // true (caution!!!)
isHTML('<>');                    // false

Wie Sie sehen, ist es alles andere als perfekt, kann aber in einigen Fällen die Arbeit für Sie erledigen.

Johan Dettmar
quelle
1
genau das, was ich brauchte. Nichts Besonderes, nur sauber. Vielen Dank!
Moeiscool
6

Alle Antworten hier sind zu umfassend, sie suchen nur nach, <gefolgt von >. Es gibt keine perfekte Möglichkeit, um festzustellen, ob eine Zeichenfolge HTML ist, aber Sie können es besser machen.

Im Folgenden suchen wir nach End-Tags , die viel enger und genauer sind:

import re
re_is_html = re.compile(r"(?:</[^<]+>)|(?:<[^<]+/>)")

Und hier ist es in Aktion:

# Correctly identified as not HTML:
print re_is_html.search("Hello, World")
print re_is_html.search("This is less than <, this is greater than >.")
print re_is_html.search(" a < 3 && b > 3")
print re_is_html.search("<<Important Text>>")
print re_is_html.search("<a>")

# Correctly identified as HTML
print re_is_html.search("<a>Foo</a>")
print re_is_html.search("<input type='submit' value='Ok' />")
print re_is_html.search("<br/>")

# We don't handle, but could with more tweaking:
print re_is_html.search("<br>")
print re_is_html.search("Foo &amp; bar")
print re_is_html.search("<input type='submit' value='Ok'>")
Geschwindigkeitsflugzeug
quelle
4

Wenn Sie einen regulären Ausdruck aus einem Zeichenfolgenliteral erstellen, müssen Sie alle Backslashes vermeiden:

var htmlRegex = new RegExp("<([A-Za-z][A-Za-z0-9]*)\\b[^>]*>(.*?)</\\1>");
// extra backslash added here ---------------------^ and here -----^

Dies ist nicht erforderlich, wenn Sie ein Regex-Literal verwenden, aber dann müssen Sie Schrägstriche vermeiden:

var htmlRegex = /<([A-Za-z][A-Za-z0-9]*)\b[^>]*>(.*?)<\/\1>/;
// forward slash escaped here ------------------------^

Außerdem hat Ihre jsfiddle nicht funktioniert, weil Sie einen onloadHandler in einem anderen onloadHandler zugewiesen haben. Die Standardeinstellung, die im linken Bereich "Frameworks & Extensions" festgelegt ist, besteht darin, den JS in einen zu verpacken onload. Ändern Sie dies in eine Nowrap-Option und korrigieren Sie das Escapezeichen des String-Literals, und es "funktioniert" (innerhalb der Einschränkungen, auf die alle in den Kommentaren hingewiesen haben): http://jsfiddle.net/wFWtc/4/

Soweit ich weiß, haben reguläre JavaScript-Ausdrücke keine Rückverweise. Also dieser Teil Ihres Ausdrucks:

</\1>

funktioniert nicht in JS (würde aber in einigen anderen Sprachen funktionieren).

nnnnnn
quelle
Nun, dies wird testen, ob eines der Tags in Ordnung aussieht, aber nichts über den Rest. Ich bin mir nicht sicher, welche Art von "Gültigkeit" das OP wünscht.
nhahtdh
1
Was ist mit <br> <hr> <input...>@ user1240679?
CSᵠ
3

/<\/?[^>]*>/.test(str) Nur erkennen, ob es HTML-Tags enthält, kann eine XML sein

leuchten
quelle
3

Mit jQuery:

function isHTML(str) {
  return /^<.*?>$/.test(str) && !!$(str)[0];
}
gtournie
quelle
2
isHTML("<foo>");// gibt true isHTML("div");zurück // gibt true zurück, wenn divs auf der Seite sind
ACK_stoverflow
@yekta - Was machst du? Dies soll überprüfen, ob die Zeichenfolge HTML ist oder nicht. Eine E-Mail ist meines Wissens kein HTML-Tag ... isHTML ('[email protected] ') -> false // korrekt
gtournie
1
Ein String kann alles sein. Wenn Sie wissen, dass es sich um ein HTML-Tag handelt, warum sollten Sie dann überprüfen, ob es sich überhaupt um HTML handelt? Ich folge Ihrem Standpunkt nicht ganz. Dies @ist keine gültige Syntax für einen Selektor. Wenn Sie es also an einen jQuery-Selektor übergeben, wird eine Ausnahme ausgelöst (dh $("[email protected]")von !!$(str)[0]). Ich beziehe mich speziell auf den !!$(str)[0] Teil. Sie haben gerade Ihre Antwort bearbeitet, aber jetzt suchen Sie nach HTML, bevor jQuery etwas unternimmt.
Yekta
Ich glaube nicht, dass der Autor überprüfen wollte, ob es nur eine Zeichenfolge war. Das ist der Punkt. Was er wollte, war eine Funktion, die überprüfen konnte, ob die Zeichenfolge ein gültiges HTML- Tag war , nicht nur HTML (ansonsten ist dies ein bisschen dumm). Ich habe meine Antwort aktualisiert, nachdem ich den Kommentar @ACK_stoverflow gelesen habe, aber ich bin sicher, dass ein einfacher regulärer Ausdruck dies tun sollte.
Gtournie
3

In diesem Fall wäre die einfachste Form bei Verwendung von jQuery:

if ($(testString).length > 0)

Wenn $(testString).length = 1 dies bedeutet, dass sich ein HTML-Tag darin befindet textStging.

Christo Peev
quelle
Berücksichtigen Sie gemäß der Antwort unten (beginnend mit "With jQuery", geschrieben vier Jahre vor diesem!) Die schlechte Auswahl mehrerer Verwendungen von einem einzigen Einstiegspunkt aus. $()ist eine CSS-Auswahloperation. Aber auch eine DOM-Node-Factory aus textueller HTML-Serialisierung. Aber auch ... gemäß der anderen Antwort, die unter der gleichen Abhängigkeit von jQuery leidet, ist "div" kein HTML, aber das würde zurückkehren, truewenn <div>Elemente auf der Seite vorhanden sind. Dies ist ein sehr, sehr schlechter Ansatz, wie ich es von fast jeder Lösung erwartet habe, die unnötig jQuery beinhaltet. (Lass es sterben.)
amcgregor
1

Es gibt ausgefallene Lösungen, bei denen der Browser selbst verwendet wird, um zu versuchen, den Text zu analysieren und festzustellen, ob DOM-Knoten erstellt wurden, was… langsam sein wird. Oder reguläre Ausdrücke, die schneller sind, aber… möglicherweise ungenau. Es gibt auch zwei sehr unterschiedliche Fragen, die sich aus diesem Problem ergeben:

Q1: Enthält eine Zeichenfolge HTML-Fragmente?

Ist die Zeichenfolge Teil eines HTML-Dokuments, das HTML-Element-Markup oder codierte Entitäten enthält? Dies kann als Indikator dafür verwendet werden, dass die Zeichenfolge möglicherweise gebleicht / bereinigt oder entifiziert werden muss:

/</?[a-z][^>]*>|(\&(?:[\w\d]+|#\d+|#x[a-f\d]+);/

Sie können dieses verwendete Muster sehen aller Beispiele aus allen zum Zeitpunkt dieses Schreibens vorhandenen Antworten sowie anhand einiger… ziemlich abscheulicher, von WYSIWYG oder Word generierter Beispieltexte und einer Vielzahl von Verweisen auf Zeichenentitäten sehen.

F2: Ist die Zeichenfolge ein HTML-Dokument?

Die HTML-Spezifikation ist schockierend locker, was ein HTML-Dokument betrifft . Browser sind extrem bemüht, fast jeden Mülltext als HTML zu analysieren. Zwei Ansätze: entweder einfach alles HTML berücksichtigen (da bei Lieferung mit einem text/htmlInhaltstyp große Anstrengungen unternommen werden, um zu versuchen , es vom Benutzeragenten als HTML zu interpretieren) oder nach dem Präfix-Marker suchen:

<!DOCTYPE html>

In Bezug auf "Wohlgeformtheit" ist das und fast nichts anderes "erforderlich". Das Folgende ist ein 100% vollständiges, vollständig gültiges HTML-Dokument, das jedes HTML-Element enthält, von dem Sie glauben, dass es weggelassen wird:

<!DOCTYPE html>
<title>Yes, really.</title>
<p>This is everything you need.

Jep. Es gibt explizite Regeln, wie „fehlende“ Elemente wie zu bilden <html>, <head>und <body>. Obwohl ich es ziemlich amüsant finde, dass die Syntaxhervorhebung von SO dies ohne einen expliziten Hinweis nicht richtig erkennen konnte.

amcgregor
quelle
0

Meine Lösung ist

const element = document.querySelector('.test_element');

const setHtml = elem =>{
    let getElemContent = elem.innerHTML;

    // Clean Up whitespace in the element
    // If you don't want to remove whitespace, then you can skip this line
    let newHtml = getElemContent.replace(/[\n\t ]+/g, " ");

    //RegEX to check HTML
    let checkHtml = /<([A-Za-z][A-Za-z0-9]*)\b[^>]*>(.*?)<\/\1>/.test(getElemContent);

    //Check it is html or not
    if (checkHtml){
        console.log('This is an HTML');
        console.log(newHtml.trim());
    }
    else{
        console.log('This is a TEXT');
        console.log(elem.innerText.trim());
    }
}

setHtml(element);
Kamrujaman Shohel
quelle
Ihr regulärer Ausdruck scheint sehr mangelhaft gegen einen umfassender Ausdruck , und erfordert eine Vorverarbeitung (der Anfang Ersatz) ist sehr bedauerlich.
Amcgregor
-1

Es gibt ein NPM-Paket is-html, das versuchen kann, dieses Problem zu lösen: https://github.com/sindresorhus/is-html

Colin D.
quelle
Ich verstehe den Ausdruck, den es zu verwenden versucht, nicht, außer beim deklarierten Doctype, und das "vollständige" Muster, das aus bekannten HTML-Elementen aufgebaut ist, die aus einer zusätzlichen Abhängigkeit gezogen wurden, ignoriert die Tatsache, dass HTML nicht so funktioniert und nicht funktioniert schon sehr, sehr lange. Darüber hinaus werden im Basismuster explizit Erwähnungen <html>und <body>Tags erwähnt, die beide völlig optional sind . Der Test "Nicht mit XML übereinstimmen" ist aussagekräftig.
Amcgregor
@amcgregor Wenn Sie der Meinung sind, dass Ihre Lösung besser ist, tragen Sie vielleicht zum isHTML-Repo bei? und fügen Sie Ihre Testsuite von regex101 hinzu? es wäre wertvoll für die Gemeinde
Colin D
Der grundlegende Zweck dieser Bibliothek ist falsch und wird in einer großen Anzahl von Fällen von Natur aus falsch sein, normalerweise durch falsches Markieren als Nicht-HTML aufgrund des Vorhandenseins von Tags, die sie nicht versteht. Die Validierung kann auf diese Weise nicht erfolgreich sein. Zusätzlich kann eine einfache regex oder eine (edit: Paar ) librar [n] ... wir , wie man Programm vergessen haben , und Node / NPM ist keine Sprache oder Toolchain ich in der Regel nutzen möchten, tragen dazu bei , oder fördern die Verwendung von .
Amcgregor
Okay, Amcgergor, du bist mir gegenüber ziemlich negativ, als ich nur versucht habe zu helfen. Ich bin nicht einverstanden mit der Prämisse, dass npm falsch ist. Stellen Sie sich vor, Ihre Antwort auf den Stapelüberlauf hat in Zukunft eine kleine Änderung ergeben. Ich als Entwickler, der Ihre Bibliothek verwendet, würde nur ein Upgrade durchführen und ein besseres Verhalten erzielen. Stattdessen muss ich ... mit dem fehlerhaften Verhalten leben oder diese Stapelüberlaufantwort erneut aufrufen, um Ihre Änderungen zu erhalten? Das ist das alternative Universum
Colin D
Negativ? Ich erklärte meine Haltung und warum ich nicht tun würde, was sonst vernünftig erscheinen würde. Beachten Sie jedoch, dass der Artikel, den ich verlinkt habe, die Fortsetzung eines etwas entzündlicheren ersten (im Voraus verlinkten) Artikels war, der viel Diskussion hervorrief. Er veröffentlichte ein technisches Papier , das ebenfalls dort verlinkt ist, nach unten. Ich kontere Ihr Bauchgefühl bei der Überarbeitung mit Beweisen für Qualität. Ref: §7.2 (& die Katastrophe auf der linken Seite & eslint)
amcgregor