Sprawdź, czy ciąg to html, czy nie


100

Mam pewien ciąg, dla którego chcę sprawdzić, czy jest to html, czy nie. Używam wyrażenia regularnego do tego samego, ale nie uzyskuję właściwego wyniku.

Sprawdziłem poprawność mojego wyrażenia regularnego i tutaj działa dobrze .

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

Oto skrzypce, ale wyrażenie regularne tam nie działa. http://jsfiddle.net/wFWtc/

Na moim komputerze kod działa poprawnie, ale w wyniku otrzymuję fałsz zamiast prawda. Czego tu brakuje?


5
Użyj parsera HTML, aby przeanalizować HTML. Przeczytaj to, jeśli jeszcze tego nie zrobiłeś.
Frédéric Hamidi

3
pytanie wciąż nadchodzi, powinien istnieć bot stosu, który automatycznie ustawi komentarz do każdego pytania z html i regex w nim
Bartłomiej Lewandowski

3
To trochę zależy od tego, jakiego poziomu zaawansowania oczekujesz od czeku. Możesz sprawdzić, czy ciąg zawiera co najmniej jeden <i co najmniej jeden >i nazwać go HTML, lub możesz sprawdzić, czy jest ściśle poprawny z poprawną składnią HTML lub cokolwiek pomiędzy. W najprostszych przypadkach parser HTML nie jest potrzebny.
JJJ

3
Dlaczego sprawdzasz, że ciąg to HTML?
nhahtdh

2
@ user1240679: Prawidłowy format znaczników? Jaki rodzaj ważności? W najściślejszym sensie potrzebujesz DTD, aby to opisać. W pewnym sensie możesz chcieć sprawdzić, czy tagi są prawidłowo dopasowane. Żaden z dwóch powyższych przypadków nie dotyczy wyrażenia regularnego.
nhahtdh

Odpowiedzi:


319

Lepszym wyrażeniem regularnym do sprawdzenia, czy ciąg jest w formacie HTML, jest:

/^/

Na przykład:

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

W rzeczywistości, to jest tak dobra, że będzie to powrót truedo każdej struny przekazany do niego, co jest, bo każdy łańcuch jest HTML . Poważnie, nawet jeśli jest źle sformatowany lub nieprawidłowy, nadal jest to HTML.

Jeśli szukasz elementów HTML, a nie zwykłego tekstu, możesz użyć czegoś takiego jak:

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

Nie pomoże ci to w żaden sposób przeanalizować kodu HTML, ale z pewnością oflaguje ciąg jako zawierający elementy HTML.


52
Jestem szczerze zaskoczony, że nie otrzymałem więcej głosów negatywnych za snarka.
zzzzBov

8
@clenemt, więc uważasz, że a < b && a > cjest to HTML?
zzzzBov

1
@zzzzBov wiesz, że uważasz, że a<b && a>cjest to HTML ... Chciałbym, aby wykrywanie HTML mogło być tak bardzo uproszczone. Parsowanie nigdy nie jest łatwe.
oriadam

2
@oriadam, kontekst służył do wykrywania elementów w tym przypadku. Jeśli używasz a < b && a > cprzeglądarki będzie włączyć >i <znaków na &gt;i &lt;jednostek odpowiednio. Jeśli zamiast tego użyjesz a<b && a>cprzeglądarki, zinterpretuje znacznik jako, a<b && a>c</b>ponieważ brak spacji oznacza, że <botwiera <b>element. Oto krótkie demo tego, o czym mówię .
zzzzBov

4
Jest to prawdopodobnie najwyżej oceniona odpowiedź trolla, jaką widziałem. ;)
aandis

74

Metoda nr 1 . Oto prosta funkcja do sprawdzenia, czy ciąg zawiera dane HTML:

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

Pomysł polega na umożliwieniu parserowi DOM przeglądarki decydowania, czy podany ciąg wygląda jak HTML, czy nie. Jak widać, po prostu sprawdza ELEMENT_NODE( nodeTypez 1).

Zrobiłem kilka testów i wygląda na to, że działa:

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

To rozwiązanie poprawnie wykryje ciąg HTML, jednak ma efekt uboczny, że img / vide / etc. Tagi zaczną pobierać zasób po przeanalizowaniu w innerHTML.

Metoda nr 2 . Inna metoda wykorzystuje DOMParser i nie ma skutków ubocznych ładowania zasobów:

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

Uwagi:
1. Array.fromto metoda ES2015, można ją zastąpić [].slice.call(doc.body.childNodes).
2. Funkcję strzałki w somewywołaniu można zastąpić zwykłą funkcją anonimową.


3
To wspaniały pomysł. Jednak ta funkcja nie mogła wykryć znacznika zamykającego (tj isHTML("</a>") --> false.).
Lewis

9
Świetne rozwiązanie! .. Jedynym negatywnym skutkiem ubocznym jest to, że jeśli Twój kod HTML zawiera statyczne zasoby, takie jak atrybut src obrazu innerHTML, zmusi przeglądarkę do rozpoczęcia pobierania tych zasobów. :(
Jose Browne

@JoseBrowne, nawet jeśli nie jest dołączony do DOM?
kuus

1
@kuus Tak, nawet jeśli nie jest dołączany. Użyj rozwiązania DOMParser.
dfsq

1
Dobry pomysł, ale czy zaakceptowana odpowiedź nie byłaby lepsza pod względem wydajności? Zwłaszcza jeśli masz ogromne struny (gra słów zamierzona) lub jeśli musisz często używać tego testu.
DerpyNerd

13

Trochę walidacji z:

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

Wyszukuje puste tagi (niektóre predefiniowane) i /zakończone puste tagi XHTML i sprawdza jako HTML z powodu pustego tagu LUB przechwyci nazwę tagu i spróbuje znaleźć zamykający tag gdzieś w ciągu, aby zweryfikować go jako HTML.

Wyjaśnione demo: http://regex101.com/r/cX0eP2

Aktualizacja:

Pełna walidacja z:

/<(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) 

To robi właściwą walidację, ponieważ zawiera ALL znaczniki HTML, puste najpierw, a następnie pozostałe, które wymagają znacznika zamykającego.

Wyjaśnione demo tutaj: http://regex101.com/r/pE1mT5


1
Uwaga: dolne wyrażenie regularne działa, ale nie wykryje niezamkniętych tagów HTML, takich jak „<strong> witaj świecie”. Oczywiście, że jest to uszkodzony kod HTML, dlatego powinien być traktowany jako ciąg znaków, ale ze względów praktycznych Twoja aplikacja może chcieć je wykryć.
TK123

HTML został zaprojektowany z myślą o przebaczeniu użytkowników. „Nieprawidłowe” tagi nie są nieprawidłowe, są po prostu nieznane i dozwolone. Atrybuty „Nieprawidłowe” nie są nieprawidłowe… Jest to szczególnie zauważalne, gdy zaczyna się stosować „komponenty sieciowe” i technologie takie jak JSX, które łączą HTML i bogatsze opisy komponentów, zazwyczaj generując cień DOM. Wrzuć to do pliku i oceń document.querySelector('strange')- zadziała.
amcgregor

(Podsumowując: ze względu na sposób, w jaki została napisana specyfikacja, próba „walidacji” znaczników HTML jest w zasadzie głupim zadaniem. Odnośnik do przykładowego dokumentu HTML z „nieprawidłowym” elementem jest w 100% w pełni uformowany, kompletny dokument HTML - i jest od 1997 roku - jako kolejny przykład.)
amcgregor

10

Powyższa odpowiedź zzzzBov jest dobra, ale nie uwzględnia przypadkowych tagów zamykających, takich jak na przykład:

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

Wersja, która również przechwytuje tagi zamykające, może wyglądać tak:

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

Mogło być lepiej zasugerować zmianę, zamiast publikować ją jako komentarz.
Zlatin Zlatev

Chyba masz na myśli <[a-z/][\s\S]*>- zwróć uwagę na ukośnik w pierwszej grupie.
Ryan Guill

7

Oto niechlujna jedna linijka, której używam od czasu do czasu:

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

Zasadniczo powróci truedo ciągów zawierających ciąg, <po którym ANYTHINGnastępuje >.

Mówiąc ANYTHING, mam na myśli w zasadzie wszystko oprócz pustego ciągu.

To nie jest świetne, ale to jedna linijka.

Stosowanie

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

Jak widać, jest to dalekie od ideału, ale w niektórych przypadkach może wykonać pracę za Ciebie.


1
dokładnie to, czego potrzebowałem. Nic wyszukanego, po prostu czysto. Dzięki!
moeiscool

6

Wszystkie odpowiedzi tutaj są zbyt wyczerpujące, po prostu szukają, <a następnie >. Nie ma idealnego sposobu na wykrycie, czy ciąg to HTML, ale możesz zrobić to lepiej.

Poniżej szukamy tagów końcowych , które będą znacznie ciaśniejsze i dokładniejsze:

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

A oto w akcji:

# 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'>")

4

Jeśli tworzysz wyrażenie regularne z literału ciągu, musisz uciec przed wszelkimi odwrotnymi ukośnikami:

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

Nie jest to konieczne, jeśli używasz literału wyrażenia regularnego, ale wtedy musisz pominąć ukośniki:

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

Również twój jsfiddle nie działał, ponieważ przypisałeś moduł onloadobsługi w innym onloadmodule obsługi - domyślnym ustawieniem w panelu Frameworks & Extensions po lewej stronie jest owinięcie JS w plik onload. Zmień to na opcję nowrap i popraw ucieczkę literału ciągu i to "działa" (w ramach ograniczeń, które każdy wskazał w komentarzach): http://jsfiddle.net/wFWtc/4/

O ile wiem, wyrażenia regularne JavaScript nie mają odniesień wstecznych. Więc ta część twojego wyrażenia:

</\1>

nie będzie działać w JS (ale będzie działać w niektórych innych językach).



Cóż, to sprawdzi, czy jeden z tagów wygląda dobrze, ale nic z resztą. Nie jestem pewien, jakiego rodzaju „ważności” chce OP.
nhahtdh

1
a co z <br> <hr> <input...>@ user1240679?
CSᵠ

3

/<\/?[^>]*>/.test(str) Wykrywaj tylko, czy zawiera tagi html, może to być xml


27 is < 42, and 96 > 42. To nie jest HTML.
amcgregor

3

Dzięki jQuery:

function isHTML(str) {
  return /^<.*?>$/.test(str) && !!$(str)[0];
}

2
isHTML("<foo>");// zwraca true isHTML("div");// zwraca true, jeśli divna stronie znajdują się
znaki

@yekta - O co ci chodzi? Ma to na celu sprawdzenie, czy ciąg znaków jest html, czy nie. O ile wiem, e-mail nie jest tagiem html ... isHTML ('foo@bar.com ') -> false // poprawne
gtournie

1
Ciąg może być wszystkim, jeśli znasz jego znacznik HTML, to po co sprawdzać, czy jest to HTML, nie do końca rozumiem twój punkt widzenia. Nie @jest prawidłową składnią selektora. Tak więc, kiedy przekażesz go do selektora jQuery, zgłosi wyjątek (np. $("you@example.com")From !!$(str)[0]). Mam na myśli konkretnie !!$(str)[0] porcję. Właśnie zredagowałeś swoją odpowiedź, ale teraz sprawdzasz HTML, zanim jQuery zrobi cokolwiek.
yekta

Myślę, że autor nie chciał sprawdzać, czy to był tylko sznurek. O to chodzi. To, czego chciał, to funkcja umożliwiająca sprawdzenie, czy ciąg jest prawidłowym tagiem HTML , a nie tylko HTML (w przeciwnym razie jest to trochę głupie). Zaktualizowałem odpowiedź po przeczytaniu komentarza @ACK_stoverflow, ale jestem pewien, że proste wyrażenie regularne powinno to zrobić.
gtournie

3

Używając jQuery w tym przypadku, najprostsza forma to:

if ($(testString).length > 0)

Jeśli $(testString).length = 1oznacza to, że w środku znajduje się jeden tag HTML textStging.


Zgodnie z odpowiedzią poniżej (zaczynając od „Z jQuery”, napisanej cztery lata wcześniej!), Rozważ słaby wybór wielu zastosowań z jednego punktu wejścia. $()jest operacją selektora CSS. Ale także fabryka węzłów DOM z serializacji tekstowej HTML. Ale także… zgodnie z drugą odpowiedzią cierpiącą na tę samą zależność od jQuery, „div” nie jest HTML-em, ale zostanie zwrócony, truejeśli <div>na stronie istnieją jakiekolwiek elementy. Jest to bardzo, bardzo złe podejście, jak się spodziewałem w przypadku prawie każdego rozwiązania niepotrzebnie wykorzystującego jQuery. (Let it die.)
amcgregor

2

Istnieją wymyślne rozwiązania polegające na wykorzystaniu samej przeglądarki do próby przeanalizowania tekstu i stwierdzenia, czy zostały zbudowane jakiekolwiek węzły DOM, co będzie… powolne. Albo wyrażenia regularne, które będą szybsze, ale… potencjalnie niedokładne. Z tego problemu wynikają również dwa bardzo różne pytania:

P1: Czy ciąg zawiera fragmenty HTML?

Czy ciąg znaków jest częścią dokumentu HTML, zawierającego znaczniki elementu HTML lub zakodowane encje? Może to służyć jako wskaźnik, że ciąg może wymagać wybielania / odkażania lub dekodowania jednostki:

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

Możesz zobaczyć ten wzorzec w użyciu we wszystkich przykładach ze wszystkich istniejących odpowiedzi w czasie tego pisania, a także trochę… raczej ohydny przykładowy tekst wygenerowany w trybie WYSIWYG lub Word i różne odniesienia do jednostek znakowych.

P2: Czy ciąg znaków jest dokumentem HTML?

Specyfikacja HTML jest szokująco luźna, jeśli chodzi o to, co uważa za dokument HTML . Przeglądarki robią wszystko, co w ich mocy, aby przeanalizować prawie każdy śmieciowy tekst jako HTML. Dwa podejścia: albo po prostu weź pod uwagę wszystko HTML (ponieważ jeśli zostanie dostarczony z text/htmltypem treści, włożony zostanie duży wysiłek, aby spróbować zinterpretować go jako HTML przez klienta użytkownika) lub poszukaj znacznika prefiksu:

<!DOCTYPE html>

Jeśli chodzi o „uformowanie”, to i prawie nic więcej nie jest „wymagane”. Poniżej znajduje się w 100% kompletny, w pełni poprawny dokument HTML zawierający każdy element HTML, który Twoim zdaniem jest pomijany:

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

Tak. Istnieją wyraźne zasady, jak tworzyć „brakujących” elementów, takich jak <html>, <head>i <body>. Chociaż wydaje mi się raczej zabawne, że podświetlanie składni SO nie wykryło tego poprawnie bez wyraźnej wskazówki.


0

Moje rozwiązanie to

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

Twoje wyrażenie regularne wydaje się wysoce wadliwe w porównaniu z bardziej wszechstronnym wyrażeniem , a wymaganie wstępnego przetwarzania (początkowej zamiany) jest wysoce niefortunne.
amcgregor

-1

Istnieje pakiet NPM is-html, który może próbować rozwiązać ten https://github.com/sindresorhus/is-html


Nie rozumiem wyrażenia, którego próbuje użyć, które kończy się niepowodzeniem poza zadeklarowanym typem dokumentu, a „pełny” wzorzec skonstruowany ze znanych elementów HTML pobranych z dodatkowej zależności ignoruje fakt, że tak nie działa HTML i nie ma było bardzo, bardzo długo. Ponadto wzorzec podstawowy wyraźnie wspomina <html>i <body>tagi, z których oba są całkowicie opcjonalne . Test „niezgodny z XML” mówi wiele.
amcgregor

@amcgregor, jeśli uważasz, że Twoje rozwiązanie jest lepsze, może dołączyć do repozytorium isHTML? i dodać swój zestaw testów z regex101? byłoby to cenne dla społeczności
Colin D

Podstawowy cel tej biblioteki jest chybiony i z natury będzie błędny w wielu przypadkach, zwykle przez fałszywe oznaczenie jako nie-HTML ze względu na obecność tagów, których nie rozumie; walidacja nie może się powieść w ten sposób. Dodatkowo, proste wyrażenie regularne lub (edit: pair of ) librar [y]… mogliśmy zapomnieć, jak programować , a Node / NPM nie jest językiem ani łańcuchem narzędzi, którego generalnie chcę używać, współtworzyć lub zachęcać do używania .
amcgregor

W porządku, amcgergor, jesteś wobec mnie dość negatywny, kiedy tylko próbowałem pomóc. Nie zgadzam się z założeniem, że npm jest chybiony. Wyobraź sobie, że w odpowiedzi na przepełnienie stosu pojawiła się mała poprawka w przyszłości. Ja, jako programista korzystający z twojej biblioteki, po prostu zaktualizowałem i uzyskałem bardziej odpowiednie zachowanie. Zamiast tego muszę ... żyć ze zepsutym zachowaniem lub ponownie odwiedzić tę odpowiedź dotyczącą przepełnienia stosu, aby uzyskać zmiany? To jest alternatywny wszechświat
Colin D

Negatywny? Tłumaczyłem swoje stanowisko i dlaczego nie robiłbym tego, co w innym przypadku wydawałoby się rozsądne. Zwróć jednak uwagę, że artykuł, który połączyłem, był kontynuacją nieco bardziej zapalnego pierwszego (połączonego z góry), który wywołał wiele dyskusji. Opublikował artykuł techniczny , również tam powiązany, na dole. Przeciwstawiam się przeczuciom dotyczącym ponownej pracy dowodami dotyczącymi jakości. Ref: §7.2 (& the left-pad disaster & eslint)
amcgregor
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.