Перевірте, чи є рядок html чи ні


98

У мене є певний рядок, для якого я хочу перевірити, це html чи ні. Я використовую регулярний вираз для того самого, але не отримую належного результату.

Я підтверджено моє регулярний вираз , і він прекрасно працює тут .

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

Ось скрипка, але регулярний вираз там не працює. http://jsfiddle.net/wFWtc/

На моїй машині код працює нормально, але в результаті я отримую false, а не true. Чого тут не вистачає?


5
Використовуйте синтаксичний аналізатор HTML для синтаксичного аналізу HTML. Будь ласка, прочитайте це, якщо ви ще цього не зробили.
Фредерік Хаміді,

3
питання продовжує надходити, там повинен бути бот стека, який буде надзвичайно встановлювати коментар до кожного питання з html та регулярним виразом у ньому
Бартломій Левандовскі

3
Це як би залежить від того, якого рівня витонченості ви хочете отримати від чека. Ви можете перевірити, чи містить рядок хоча б один <і принаймні один, >і назвати його HTML, або ви можете перевірити, чи є він строго дійсним, використовуючи правильний синтаксис HTML або щось середнє. У найпростіших випадках синтаксичний аналізатор HTML не потрібен.
JJJ

2
Чому ви перевіряєте рядок як HTML?
nhahtdh

2
@ user1240679: Дійсний формат розмітки? Який термін дії? У найсуворішому сенсі вам потрібен DTD для його опису. У вільному сенсі ви можете перевірити, чи правильно підібрані теги. Будь-який із наведених вище випадків не є роботою для регулярних виразів.
nhahtdh

Відповіді:


315

Кращим регулярним виразом для перевірки, чи є рядок HTML:

/^/

Наприклад:

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

Насправді, це настільки добре, що він повернеться trueдля кожного переданого йому рядка, тому що кожен рядок є HTML . Серйозно, навіть якщо він погано відформатований або недійсний, це все одно HTML.

Якщо ви шукаєте наявність елементів HTML, а не просто будь-який текстовий вміст, ви можете використати щось на зразок:

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

Це ніяк не допоможе вам проаналізувати HTML, але це, безумовно, позначить рядок як такий, що містить елементи HTML.


47
Я щиро здивований, що я не отримав більше прихильників за заїзд.
zzzzBov

7
@clenemt, тож ви вважаєте a < b && a > cHTML?
zzzzBov

1
@zzzzBov, ти знаєш, що вважаєш a<b && a>cсебе HTML ... Я б хотів, щоб виявлення HTML могло настільки спроститися. Розбір ніколи не буває простим.
оріадам

2
@oriadam, контекст був для виявлення елементів у такому випадку. Якщо ви використовуєте a < b && a > cбраузер повертати >і <символи в &gt;і &lt;особа належним чином . Якщо замість цього ви використовуєте a<b && a>cбраузер, інтерпретація розмітки буде a<b && a>c</b>спричинена тим, що відсутність простору означає <bвідкриття <b>елемента. Ось коротка демонстрація того, про що я говорю .
zzzzBov

4
Це, мабуть, найвища голосова відповідь тролів, яку я бачив на цьому. ;)
aandis

72

Спосіб No1 . Ось проста функція для перевірки, чи містить рядок дані 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;
}

Ідея полягає в тому, щоб дозволити синтаксичному аналізатору браузера DOM вирішити, чи наданий рядок виглядає як HTML чи ні. Як бачите, він просто перевіряє наявність ELEMENT_NODE( nodeTypeз 1).

Я зробив пару тестів і, схоже, це працює:

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

Це рішення буде правильно виявляти рядок HTML, однак воно має побічний ефект, що img / vide / etc. теги почнуть завантажувати ресурс після аналізу у innerHTML.

Спосіб No2 . Інший метод використовує DOMParser і не має побічних ефектів завантаження ресурсів:

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

Примітки:
1. Array.fromце метод ES2015, його можна замінити на [].slice.call(doc.body.childNodes).
2. Функцію стрілки у someвиклику можна замінити звичайною анонімною функцією.


3
Це неймовірна ідея. Однак ця функція не може виявити закриває тег (тобто isHTML("</a>") --> false).
Льюїс

9
Чудове рішення! .. Єдиним негативним побічним ефектом є те, що якщо ваш html містить будь-які статичні ресурси, такі як атрибут image src .. innerHTML, змусить браузер почати вибір цих ресурсів. :(
Jose Browne 02

@JoseBrowne, навіть якщо він не доданий до DOM?
kuus

1
@kuus Так, навіть якщо він не додається. Використовуйте розчин DOMParser.
dfsq

1
Хороша ідея, але чи не буде прийнята відповідь кращою для виступу? Особливо, якщо у вас величезні струни (задумано каламбур) або якщо вам доведеться багато використовувати цей тест.
DerpyNerd

13

Трохи перевірки за допомогою:

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

Це шукає порожні теги (деякі заздалегідь визначені) та /завершені порожні теги XHTML і перевіряє як HTML через порожній тег АБО захоплює ім'я тегу та намагається знайти його закриває тег десь у рядку, щоб перевірити як HTML.

Пояснена демонстрація: http://regex101.com/r/cX0eP2

Оновлення:

Повна перевірка за допомогою:

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

Це робить правильну перевірку, оскільки містить ВСІ теги HTML, спочатку порожні, а потім інші, які потребують закриваючого тегу.

Пояснення демонстрації тут: http://regex101.com/r/pE1mT5


1
Просто зверніть увагу, що нижній регулярний вираз справді працює, але він не виявить незакритих тегів html, таких як "'<strong> привіт, світ". якщо це пошкоджено html, тому слід розглядати його як рядок, але з практичних цілей ваш додаток може захотіти виявити і їх.
TK123

HTML розроблений з урахуванням прощення користувацьких агентів. Теги "недійсні" не є недійсними, вони просто невідомі та дозволені. "Недійсні" атрибути не є недійсними ... Це особливо помітно, коли починають залучати "веб-компоненти" та такі технології, як JSX, які поєднують HTML та більш багаті описи компонентів, як правило, створюючи тіньовий DOM. Пошляпніть це у файл і виправте document.querySelector('strange')- це буде працювати.
amcgregor

(Підсумовуючи: завдяки тому, як пишеться специфікація, спроба "перевірити" розмітку HTML є, по суті, помилковим завданням. Посилання на зразок HTML-документа з "недійсним" елементом є 100% повністю сформованим, повний HTML-документ - і, як інший приклад, з 1997 р.)
amcgregor

9

Відповідь zzzzBov вище хороша, але вона не враховує збиткові закриваючі теги, як наприклад:

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

Версія, яка також ловить закриваючі теги, може бути такою:

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

Можливо, було б краще запропонувати редагування, а не публікувати це як коментар.
Златін Златев

Думаю, ви маєте на увазі <[a-z/][\s\S]*>- зверніть увагу на скісну риску в першій групі.
Райан Гілл,

7

Ось недбалий однокласник, яким я час від часу користуюся:

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

В основному він повертається trueдля рядків, що містять <наступні та ANYTHINGнаступні >.

Під ANYTHING, я маю в виду в основному нічого , крім порожнього рядка.

Це не чудово, але це однокласник.

Використання

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

Як бачите, це далеко не ідеально, але в деяких випадках може зробити роботу за вас.


1
саме те, що мені потрібно. Нічого химерного, просто чисте. Дякую!
moeiscool

6

Всі відповіді тут надмірно широкі, вони просто шукають, <а потім і наступні >. Не існує ідеального способу визначити, чи є рядок HTML, але ви можете зробити це краще.

Нижче ми шукаємо кінцеві теги , і ми будемо набагато жорсткішими та точнішими:

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

І ось воно в дії:

# 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

Якщо ви створюєте регулярний вираз із рядкового літералу, вам потрібно уникнути будь-яких зворотних скісних рисок:

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

Це не потрібно, якщо ви використовуєте літерал регулярного виразу, але тоді вам потрібно уникнути похилих рисок:

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

Також ваш jsfiddle не працював, тому що ви призначили onloadобробник всередині іншого onloadобробника - за замовчуванням, встановленим на панелі Frameworks & Extensions зліва, є обгортання JS у файл onload. Змініть це на параметр nowrap і виправте рядок, що витікає, і він "працює" (в рамках обмежень, на які всі вказували в коментарях): http://jsfiddle.net/wFWtc/4/

Наскільки я знаю, регулярні вирази JavaScript не мають зворотних посилань. Отже, ця частина вашого виразу:

</\1>

не працюватиме в JS (але працюватиме на деяких інших мовах).



Ну, це перевірить, що один із тегів виглядає нормально, але про решту нічого. Не впевнений, яку саме «дійсність» бажає OP.
nhahtdh

1
а як щодо <br> <hr> <input...>@ user1240679?
CSᵠ

3

/<\/?[^>]*>/.test(str) Виявити лише те, чи містить він теги html, може бути xml


27 is < 42, and 96 > 42. Це не HTML.
amcgregor

3

За допомогою jQuery:

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

2
isHTML("<foo>");// повертає true isHTML("div");// повертає true, якщо divна сторінці є s
ACK_stoverflow

@yekta - Про що ти берешся? Це повинно перевірити, чи є рядок html чи ні. Електронна пошта, наскільки мені відомо, не є тегом html ... isHTML ('foo@bar.com ') -> false // правильно
gtournie

1
Рядок може бути будь-яким, якщо ви знаєте його HTML-тег, то навіщо перевіряти, чи є його HTML спочатку, я не зовсім дотримуюся вашої думки. @Чи не є коректним синтаксис для вибору. Таким чином, коли ви передаєте його селектору jQuery, він видасть виняток (тобто $("you@example.com")з !!$(str)[0]). Я спеціально маю на увазі !!$(str)[0] частину. Ви щойно відредагували свою відповідь, але зараз ви перевіряєте наявність HTML перед тим, як jQuery щось зробить.
єкта

Не думаю, що автор хотів перевірити, чи це був просто рядок. В тім-то й річ. Що він хотів, це функція, здатна перевірити, чи рядок є дійсним тегом HTML , а не лише HTML (інакше це трохи дурно). Я оновив свою відповідь після прочитання коментаря @ACK_stoverflow, але я впевнений, що це повинен зробити простий регулярний вираз.
gtournie

3

Використовуючи jQuery в цьому випадку, найпростішою формою буде:

if ($(testString).length > 0)

Якщо $(testString).length = 1це означає, що всередині є один тег HTML textStging.


Відповідно до відповіді трохи нижче (починаючи з "With jQuery", написаної за чотири роки до цього!), Розгляньте поганий вибір декількох застосувань з однієї точки входу. $()є операцією селектора CSS. Але також фабрика вузлів DOM із серіалізації текстових HTML. Але також ... відповідно до іншої відповіді, яка страждає від тієї ж залежності від jQuery, "div" - це не HTML, але це повернеться, trueякщо <div>на сторінці є якісь елементи. Це дуже, дуже поганий підхід, як я вже очікував, майже з будь-яким рішенням, яке без потреби включає jQuery. (Нехай помре.)
amcgregor

1

Існують вигадливі рішення, які передбачають використання самого браузера для спроби синтаксичного аналізу тексту, визначаючи, чи були побудовані будь-які DOM-вузли, які будуть… повільними. Або регулярні вирази, які будуть швидшими, але ... потенційно неточними. З цієї проблеми також виникають два дуже чітких питання:

Q1: Чи містить рядок фрагменти HTML?

Чи є рядок частиною документа HTML, що містить розмітку елементів HTML або закодовані сутності? Це може бути використано як індикатор того, що рядок може вимагати відбілювання / санітарії або декодування об'єкта:

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

Ви можете бачити, як цей шаблон використовується на всіх прикладах з усіх існуючих відповідей на момент написання статті, а також деяких ... досить огидних зразків тексту, створених WYSIWYG або Word, та різноманітних посилань на сутності символів.

Q2: Чи є рядок документом HTML?

Специфікація HTML вражаюче вільна щодо того, що вона вважає документом HTML . Браузери докладають максимум зусиль, щоб проаналізувати майже будь-який текст сміття як HTML. Два підходи: або просто розглянути все HTML (оскільки, якщо воно поставляється з text/htmlContent-Type, значні зусилля будуть витрачені на спробу інтерпретувати його як HTML користувальницьким агентом), або шукати маркер префікса:

<!DOCTYPE html>

З точки зору "добре сформованості", і майже нічого іншого "не вимагається". Далі подано 100% повний, повністю дійсний HTML-документ, що містить кожен елемент HTML, який, на вашу думку, пропущено:

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

Так. Є чіткі правила про те , як сформувати «відсутні» елементи , такі як <html>, <head>, і <body>. Хоча мені здається досить забавним, що підсвічування синтаксису SO не вдалося правильно це виявити без явного підказки.


0

Моє рішення

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

Ваш регулярний вираз здається дуже дефектним порівняно з більш повним висловом , і вимагати попередньої обробки (первинна заміна) дуже шкода.
amcgregor

-1

Існує пакет NPM is-html, який може спробувати вирішити це https://github.com/sindresorhus/is-html


Я не розумію вираз, який він намагається використати, який не вдається, за винятком оголошеного типу документа, а "повний" шаблон, побудований з відомих елементів HTML, витягнутих з додаткової залежності, ігнорує той факт, що HTML не працює і не має було дуже-дуже довго. Крім того, у базовому шаблоні явно згадуються <html>та <body>додаються теги, які обидва не є обов’язковими . Тест "не відповідає XML" показовий.
amcgregor

@amcgregor, якщо ти вважаєш, що твоє рішення краще, можливо, внести свій вклад у репозиторій isHTML? і додати свій набір тестів з regex101? це було б цінним для громади
Колін Д.

Фундаментальне призначення цієї бібліотеки є помилковим і, по суті, буде помилковим у великій кількості випадків, як правило, шляхом помилкового позначення як не HTML через наявність тегів, які вона не розуміє; перевірка не може бути успішною таким чином. Крім того, простий регулярний вираз або (редагування: пара ) бібліотекарів… ми, можливо, забули, як програмувати , і Node / NPM - це не мова чи ланцюжок інструментів, які я загалом хочу використовувати, сприяти або заохочувати використання .
amcgregor

Добре amcgergor, ти до мене ставишся досить негативно, коли я просто намагався допомогти. Я не погоджуюся з передумовою помилкового керування npm. Уявіть, що ваша відповідь на переповнення стека придумала невеликий хит у майбутньому. Я, як розробник, який використовує вашу бібліотеку, просто зробив би оновлення та отримав би більш правильну поведінку. Натомість я повинен .... жити з порушеною поведінкою або переглянути цю відповідь на переповнення стека, щоб отримати ваші зміни? Це альтернативний всесвіт
Колін Д,

Негативні? Я пояснював свою позицію і те, чому я не буду робити те, що в іншому випадку здається розумним. Однак зауважте, що стаття, до якої я посилався, була продовженням із трохи більш запального першого (пов'язаного вперед), що викликало багато дискусій. Він опублікував технічний документ , також пов'язаний там, внизу. Я протиставляю ваші почуття кишечника щодо повторної роботи доказами щодо якості. Посилання: §7.2 (& катастрофа та лінія на лівій панелі)
amcgregor
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.