RegEx збігається з відкритими тегами, за винятком автономних тегів XHTML


1473

Мені потрібно зіставити всі ці вступні теги:

<p>
<a href="foo">

Але не ці:

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

Я придумав це і хотів переконатися, що я все правильно зрозумів. Я лише захоплюю a-z.

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

Я вважаю, що це говорить:

  • Знайдіть менше, ніж тоді
  • Знайдіть (і захопіть) az один чи кілька разів, потім
  • Знайдіть нуль або більше пробілів
  • Знайдіть будь-який символ нуль або більше разів, жадібний, за винятком /, тоді
  • Знайдіть більше, ніж

Чи маю я це право? І ще важливіше, що ви думаєте?

Відповіді:


4417

Ви не можете розібрати [X] HTML за допомогою регулярного вираження. Оскільки HTML не може бути проаналізований за допомогою регулярного вираження. Regex - це не інструмент, який можна використовувати для правильного розбору HTML. Як я вже не раз відповів на питання HTML-і-regex, використання регулярного виразу не дозволить вам споживати HTML. Регулярні вирази - це інструмент, недостатньо складний для розуміння конструкцій, використовуваних HTML. HTML не є звичайною мовою, і тому його не можна розбирати регулярними виразами. Запити Regex не облаштовані для розбиття HTML на його змістовні частини. так багато разів, але це не дістається мені. Навіть розширені нерегулярні регулярні вирази, які використовуються Perl, не вирішують завдання розбору HTML. Ви ніколи не змусите мене тріснути. HTML - мова достатньої складності, що її неможливо розібрати за допомогою регулярних виразів. Навіть Джон Скіт не може розібрати HTML, використовуючи регулярні вирази. Кожного разу, коли ви намагаєтеся розібрати HTML за допомогою регулярних виразів, нечесна дитина плаче кров діви, а російські хакери переривають ваш веб-сайт. Розбір HTML за допомогою регулярного виклику викликає заплямованих душ у царство живих. HTML і регулярний вислів поєднуються разом, як кохання, шлюб та ритуальні дітища. <center> не може утримати це занадто пізно. Сила регулярного вираження та HTML в одному концептуальному просторі зруйнує ваш розум, як стільки водянистої шпаклівки. Якщо ти розбираєш HTML з допомогою регулярних виразів, ти передаєш їх та їхні богохульні способи, які прирікають усіх нас на нелюдський труд для Того, чиє ім'я не може бути виражене в Основній багатомовній площині, він приходить. HTML-плюс-regexp ліквідує n erves живих, поки ви спостерігаєте, ваша психіка в'яне в натиску жаху.занадто пізно, вже занадто пізно, ми не можемо врятувати перенесення чилі, що забезпечує регулярний вираз споживання всієї живої тканини (за винятком HTML, який він не може, як раніше пророкували) шановний пане, допоможе нам, як хто може пережити цю напасть, використовуючи регулярний вираз для розбору HTML прирекли людство до вічності страшних тортур і безпеки отворів з допомогою REGE х в якості інструменту для процесу HTML встановлює Brea ч між цим світом і страшним царству корумпованих осіб (як суб'єкти SGML, але більш корумпований) а glimp просто ПСА світ рег колишні аналізатори для HTML буде іни tantly транспортної ап свідомості rogrammer, я Nto ав ORL d безперервних кричати, він приходить, То докучливий сл ithy регулярного вираження-інфекція Віль л пожирає ваш HT ML парсеру, застосування і існування для всіх часів , як Visual Basic тільки гірше , він приходить він кому ес НЕ фі GHT ч е ПРИХОДИТЬ, привіт з нечестивою Radiance де stro҉ying все просвітлення, HTML теги Підтікання fr̶ǫm YO Ur очей , як Жика UID р айн, пісня ЧЕРГОВОГО ехра повторно ssion розбір буде Exti nguish голосу мор тал людини від зр тут я можу бачити , що ви можете побачити це гарний т він е inal snufFing про ї брехні S Мен все розгублено A LL I SLОСТ - е е PONY він прийшов сек він CoM ес він З мною з т він ич або permeat ес ал л MY FAC E MY FACE ᵒh бог п про NO Нио O ON Θ стоп т він * ̶͑̾̾ Gl ÉS ͎a̧͈͖r̽̾̈́͒͑eн О.Т. rè̑ͧ̌aͨl̘̝̙̃ͤ͂̾̆ ZA̡͊͠͝LGΌ ISͮ̂҉̯͈͕̹̘̱ T O͇̹̺ͅƝ̴ȳ̳ TH̘ Ë͖́̉ ͠P̯͍̭O̚ N̐Y̡ H̸̡̪̯ͨ͊̽̅̾̎Ȩ̬̩̾͛ͪ̈́̀́͘ ̶̧̨̱̹̭̯ͧ̾ͬC̷̙̲̝͖ͭ̏ͥͮ͟Oͮ͏̮̪̝͍M̲̖͊̒ͪͩͬ̚̚͜Ȇ̴̟̟͙̞ͩ͌͝ S̨̥̫͎̭ͯ̿̔̀ͅ


Чи намагалися ви замість цього використати аналізатор XML?


Примітка модератора

Ця публікація заблокована, щоб запобігти невідповідним редагуванням її вмісту. Повідомлення виглядає саме так, як і повинно виглядати - проблем із його змістом немає. Будь ласка, не позначте це для нашої уваги.


179
Кобі: Я думаю, що мені прийшов час залишити посаду помічника не розбирати HTML з офіцером Regex. Незалежно від того, скільки разів ми це говоримо, вони не перестануть приходити щодня ... навіть кожну годину. Це втрачена справа, з якою ще хтось може трохи поборотися. Тож продовжуйте, розбирайте HTML за допомогою регулярного виразу. Це лише зламаний код, а не життя та смерть.
bobince

27
Чи можна використовувати RegEx для розбору цієї відповіді?
Кріс Портер

2
Якщо ви не можете бачити цю публікацію, ось її скріншот у всій красі: imgur.com/gOPS2.png
Ендрю Кітон

3247

Хоча довільний HTML із лише регулярним виразом неможливий, іноді доцільно використовувати їх для розбору обмеженого, відомого набору HTML.

Якщо у вас є невеликий набір HTML-сторінок, з яких ви хочете скребти дані, а потім запустити їх у базу даних, регекси можуть працювати чудово. Наприклад, я нещодавно хотів отримати імена, партії та округи австралійських федеральних представників, які я зняв із веб-сайту парламенту. Це була обмежена, разова робота.

Regexes працював для мене чудово, і вони дуже швидко налаштувалися.


131
Крім того, скреблінг досить регулярно відформатованих даних з великих документів буде ШМЕ швидше з розумним використанням сканування та регулярного вираження, ніж будь-який загальний аналізатор. І якщо вам зручно кодувати реджекси, швидше кодуйте, ніж кодуйте xpaths. І майже напевно менш крихкі до змін у тому, що ви чистите. Так bleh.
Майкл Джонстон

255
@MichaelJohnston "Менш тендітний"? Майже точно не. Regexes піклуються про деталі форматування тексту, ніж аналізатор XML може мовчки ігнорувати. Переключення між &foo;кодуванням та CDATAсекціями? Використовуючи HTML minifier, щоб видалити пробіл у вашому документі, який браузер не надає? Аналізатор XML не піклується, і не буде добре написана операція XPath. З іншого боку, "парсер" на основі регулярних виразів ...
Чарльз Даффі

41
@CharlesDuffy для одноразової роботи це нормально, а для пробілів ми використовуємо \ s +
квант

68
@xiaomao дійсно, якщо, маючи знати про всі проблеми та шляхи вирішення проблеми, щоб отримати 80% рішення, яке не вдається залишити час "працює для вас", я не можу вас зупинити. Тим часом я перебуваю на своїй стороні огорожі, використовуючи аналізатори, які працюють на 100% синтаксично допустимого XML.
Чарльз Даффі

374
Мені колись довелося витягувати деякі дані з ~ 10 тис. Сторінок, усі з тим самим шаблоном HTML. Вони були заповнені HTML-помилками, через які парсери задушувались, і вся їх стилізація була вбудованою або з <font>тощо: відсутність класів чи ідентифікаторів, які допомогли б орієнтуватися в DOM. Після цілого дня боротьби з "правильним" підходом я нарешті перейшов до рішення регулярного викиду і змусив його працювати протягом години.
Пол A Jungwirth

2037

Я думаю, що тут недоліком є ​​те, що HTML - це граматика Чомського типу 2 (граматика без контексту), а RegEx - граматика Чомського типу 3 (звичайна граматика) . Оскільки граматика типу 2 принципово складніша за граматику типу 3 (див. Ієрархію Хомського ), математично неможливо проаналізувати XML з RegEx.

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


225
ОП просить проаналізувати дуже обмежений підмножина тегів XHTML: start. Що робить (X) HTML CFG - це його потенціал мати елементи між початковим і кінцевим тегами інших елементів (як у граматичному правилі A -> s A e). (X) HTML не має цього властивості в стартовому тезі: тег запуску не може містити інших початкових тегів. Підмножина, яку ОП намагається проаналізувати - це не CFG.
LarsH

101
У теорії CS регулярні мови є суворим набором без контекстних мов, але реалізація регулярних виразів у основних мовах програмування є більш потужною. Як описує noulakaz.net/weblog/2007/03/18/… , так звані "регулярні вирази" можуть перевіряти наявність простих чисел в одинакових, що, безумовно, є те, чого регулярний вираз із теорії CS не може виконати.
Адам Міхалчин

11
@eyelidlessness: те саме "тільки якщо" стосується всіх CFG, чи не так? Тобто, якщо вхід (X) HTML не сформований, навіть надійний аналізатор XML не буде надійно працювати. Можливо, якщо ви наводите приклади синтаксису помилок синтаксису HTML (X), реалізованого в агентах користувача реального світу ", на які ви посилаєтесь, я зрозумію, до чого ви йдете.
LarsH

82
@AdamMihalcin - це абсолютно правильно. Більшість існуючих двигунів регулярних виразів є більш потужними, ніж граматики Хомського типу 3 (наприклад, не жадібна відповідність, зворотна рефлекс). Деякі двигуни-регекси (наприклад, Perl) закінчують Тьюрінг. Це правда, що навіть це погані інструменти для розбору HTML, але цей цитований часто аргумент не є причиною.
сумнівним

27
Тут є найбільш "повний і короткий" відповідь. Це спонукає людей вивчати основи формальних граматик та мов, і, сподіваємось, математику, щоб вони не витрачали часу на безнадійні речі, як, наприклад, розв’язування задач NP в поліномічний час
mishmashru

1332

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

  1. Розв’яжіть задачу зупинки.
  2. Квадратне коло.
  3. Опрацюйте проблему продавця подорожі в O (log n) або менше. Якщо це більше, у вас закінчиться оперативна пам’ять, і двигун зависне.
  4. Шаблон буде досить великим, тому переконайтеся, що у вас є алгоритм, який без втрат стискає випадкові дані.
  5. Майже там - просто розділити всю річ на нуль. Простенька.

Я ще не зовсім закінчив останню частину, але знаю, що наближаюся. Він постійно кидає CthulhuRlyehWgahnaglFhtagnExceptions з якоїсь причини, тому я збираюся перенести його на VB 6 і використовувати On Error Resume Next. Я оновлюсь кодом, як тільки досліджу цю дивну двері, що щойно відчинилася в стіні. Хм.

PS П'єр де Ферма також придумав, як це зробити, але маржа, про яку він писав, була недостатньо великою для коду.


80
Поділ на нуль - набагато легша проблема, ніж інші, про яких ви згадуєте. Якщо ви використовуєте інтервали, а не просту арифметику з плаваючою точкою (якою має бути кожен, але ніхто не є), ви можете радісно розділити щось на [інтервал, що містить] нуль. Результат - просто інтервал, що містить плюс і мінус нескінченність.
rjmunro

148
Проблема невеликих запасів Fermat була вирішена м'якими полями в сучасному програмному забезпеченні для редагування тексту.
kd4ttc

50
Проблему з невеликою маржею Ферма вирішив Рандалл Манро, встановивши розмір шрифту на нуль: xkcd.com/1381
heltonbiker

29
FYI: Проблема Ферма була фактично вирішена в 1995 році , і для цього знадобилося лише математикам 358 років.
jmiserez

10
Мені вдалося обійти цей клейкий крок за поділом на нуль, замість цього скориставшись броунівськими храпочками, отриманими від холодного синтезу ... хоча це працює лише тоді, коли я видалю космологічну константу.
Тім Ленер

1072

Відмова : використовуйте аналізатор, якщо є можливість. Це сказало ...

Це регулярний вираз, який я використовую (!), Щоб відповідати тегам HTML:

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

Це може бути не ідеально, але я провів цей код через багато HTML. Зауважте, що він навіть ловить такі дивні речі <a name="badgenerator"">, які з’являються в Інтернеті.

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

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

або просто поєднувати, якщо і ні.

Для речей: Це робочий код із фактичного продукту. Я сумніваюся, що хтось, хто читає цю сторінку, складе враження, що використовувати регулярні вирази в HTML соціально прийнятно.

Застереження : Я мушу зазначити, що цей регулярний вираз все ще руйнується за наявності блоків CDATA, коментарів та елементів сценарію та стилю. Хороша новина - ви можете позбутися тих, хто використовує регулярний вираз ...


94
Я б пішов з чимось, що працює над розумними речами, ніж плаче про те, щоб не бути ідеально повсюдно :-)
prajeesh kumar

55
Хтось використовує CDATA всередині HTML?
Дунайський матрос

16
таким чином, ви насправді не вирішуєте проблему розбору лише з regexp, але як частина аналізатора це може працювати. PS: робочий продукт не означає хороший код. Без образи, але саме так працює промислове програмування і отримує свої гроші
mishmashru

32
Ваші регулярні вирази починається збій на дуже короткий, дійсному HTML: <!doctype html><title><</title>. Прості '<!doctype html><title><</title>'.match(/<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>/g)повернення, ["<!doctype html>", "<title>", "<</title>"]поки повинні ["<title>", "</title>"].

2
якщо ми просто намагаємося співставити та не відповідати наведеним прикладам, /<.( evidence^r> обвинувачений^>:11*)?>/g працює :-) // javascript: '<p> <a href = "Foo"> <br /> <годину клас = "Foo" />'.match(/<.([^r>][^>]*)?>/g)
МАЗМ

506

Є люди, які скажуть вам, що Земля кругла (або можливо, що Земля - ​​це сплетений сфероїд, якщо вони хочуть використовувати дивні слова). Вони брешуть.

Є люди, які скажуть вам, що регулярні вирази не повинні бути рекурсивними. Вони вас обмежують. Їм потрібно підкорити вас, і вони роблять це, тримаючи вас у невігласі.

Ви можете жити в їхній реальності або приймати червону таблетку.

Як і лорд-маршал (він родич класу. Маршал .NET?), Я бачив Regex-Verse на основі підводного стека і повернувся з знаннями про сили, які ви не уявляєте. Так, я думаю, що їх захищав Старий чи два, але вони дивилися футбол по телевізору, тому це було не складно.

Я думаю, що випадок XML досить простий. RegEx (у синтаксисі .NET), спущений та закодований у base64 для полегшення розуміння вашим слабким розумом, повинен бути таким:

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

Параметри, які потрібно встановити, є RegexOptions.ExplicitCapture. Ви шукаєте групу захоплення ELEMENTNAME. Якщо група захоплення ERRORне порожня, сталася помилка розбору і Regex зупинився.

Якщо у вас виникли проблеми з перетворенням його в читаний для людини регулярний вираз, це повинно допомогти:

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

Якщо ви не впевнені, ні, я не жартую (але, можливо, брешу). Він буде працювати. Я створив тонни тестових одиниць, щоб перевірити її, і я навіть використав (частину) тестів на відповідність . Це токенізатор, а не повномасштабний аналізатор, тому він лише розділить XML на складові маркери. Він не буде аналізувати / інтегрувати DTD.

О, якщо ви хочете вихідний код регулярного виразу, за допомогою деяких допоміжних методів:

regex для токенізації xml або повного простого регулярного виразу


68
Добрий Господи, це масово. Моє найбільше питання - чому? Ви розумієте, що всі сучасні мови мають XML-аналізатори, правда? Ви можете зробити все це, як 3 рядки, і бути впевненим, що це спрацює. Крім того, ви також розумієте , що чисто формальним виразом доказово НЕ в змозі зробити певні речі? Якщо ви не створили гібридний регекс / імперативний аналізатор коду, але це виглядає не так, як у вас. Чи можете ви також стиснути випадкові дані?
Джастін Морган

113
@Justin мені не потрібна причина. Це можна було зробити (і це було не незаконно / аморально), тому я це зробив. Для розуму немає обмежень, окрім тих, які ми визнаємо (Наполеонівський пагорб) ... Сучасні мови можуть розбирати XML? Дійсно? І я подумав, що ТО це незаконно! :-)
xanatos

76
Сер, я переконаний. Я буду використовувати цей код як частину ядра для моєї машини вічного руху - чи можете ви повірити, що дурні в патентному відомстві продовжують відхиляти мою заявку? Ну, я їх покажу. Я їх усім покажу!
Джастін Морган

31
@Justin Отже, аналізатор Xml - це визначення за вагою, а Regex - ні? Тому що якщо аналізатор Xml не визначає помилок за визначенням, може бути xml, який змушує його вийти з ладу, і ми повернемось до кроку 0. Скажімо так: і XML-аналізатор, і цей Regex намагаються розібрати всі "легальні" "XML. Вони МОЖУТЬ розбирати деякі "незаконні" XML. Клопи можуть зірвати їх обох. C # XmlReader, безумовно, більш перевірений, ніж цей Regex.
xanatos

31
Ні, нічого не містить помилок: 1) Усі програми містять принаймні одну помилку. 2) Усі програми містять принаймні один рядок непотрібного вихідного коду. 3) За допомогою №1 та №2 та за допомогою логічної індукції довести, що будь-яка програма може бути зведена до одного рядка коду з помилкою. (від Learning Perl)
Скотт Вівер

299

У оболонці ви можете розібрати HTML за допомогою sed :

  1. Turing.sed
  2. Написати HTML-аналізатор (домашнє завдання)
  3. ???
  4. Прибуток!

Пов’язане (чому ви не повинні використовувати збіг регулярних виразів):


3
Боюся, ви не отримали жарту, @kenorb. Прочитайте ще раз запитання та прийняту відповідь. Йдеться не про інструменти розбору HTML в цілому, ні про інструменти оболонки для синтаксичного розбору HTML, це про аналіз HTML через регекси.
Палець

1
Ні, @ Abdul. Це повністю, доказово (в математичному сенсі) неможливо.
Палець

3
Так, ця відповідь добре підсумовує, @Abdul. Зауважте, що реалізація регулярних виразів насправді не є регулярними виразами в математичному сенсі - у них є конструкції, які роблять їх сильнішими, часто повними Тьюрінгом (еквівалентно граматикам типу 0). Цей аргумент суперечить цьому факту, але все-таки дещо справедливий у тому сенсі, що ніколи не передбачалося, що регулярні вирази не здатні виконувати таку роботу.
Палець

2
І, до речі, жарт, на який я посилався, був змістом цієї відповіді перед (радикальними) редакціями kenorb, зокрема, редакцією 4, @ Abdul.
Палець

3
Найсмішніше те, що OP ніколи не просив розбирати html за допомогою regex. Він попросив співставити текст (який, як буває, HTML), використовуючи регулярний вираз. Що цілком розумно.
Параліфе

274

Я погоджуюся, що правильний інструмент для розбору XML і, особливо, HTML, є синтаксичним аналізатором, а не механізмом регулярного вираження. Однак, як зазначали інші, іноді використовувати регулярний вираз - це швидше, простіше і виконує роботу, якщо ви знаєте формат даних.

Microsoft насправді має розділ найкращих практик регулярних виразів у .NET Framework і конкретно розповідає про розгляд джерела вводу .

Регулярні вирази мають обмеження, але чи Ви врахували таке?

Рамка .NET унікальна, коли мова йде про регулярні вирази тим, що вона підтримує визначення балансуючої групи .

З цієї причини я вважаю, що ви МОЖЕТЕ розбирати XML, використовуючи регулярні вирази. Зауважте, що він повинен бути дійсним XML ( браузери дуже прощають HTML і дозволяють поганий синтаксис XML всередині HTML ). Це можливо, оскільки "Визначення групи балансування" дозволить механізму регулярного вираження діяти як КПК.

Цитата з цитованої вище статті 1:

.NET двигун регулярних виразів

Як описано вище, правильно врівноважені конструкції не можуть бути описані регулярним виразом. Однак двигун регулярних виразів .NET пропонує кілька конструкцій, які дозволяють розпізнати врівноважені конструкції.

  • (?<group>) - висуває захоплений результат на стек захоплення з групою імен.
  • (?<-group>) - вискакує найпопулярніший знімок із групою імен поза стеком захоплення.
  • (?(group)yes|no) - відповідає частині так, якщо існує група з групою імен, інакше не відповідає жодній частині.

Ці конструкції дозволяють регулярному вираженню .NET імітувати обмежений КПК, по суті дозволяючи прості версії операцій стеку: push, pop та порожні. Прості операції в значній мірі еквівалентні приросту, зменшенню і порівняно з нулем відповідно. Це дозволяє механізму регулярних виразів .NET розпізнавати підмножину безконтекстних мов, зокрема тих, для яких потрібен лише простий лічильник. Це в свою чергу дозволяє нетрадиційним регулярним виразам .NET розпізнавати окремі правильно збалансовані конструкції.

Розглянемо наступний регулярний вираз:

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

Використовуйте прапори:

  • Однолінійний
  • IgnorePatternWhitespace (не потрібно, якщо ви згортаєте регулярний вираз і видаляєте всі пробіли)
  • IgnoreCase (не потрібно)

Пояснений регулярний вираз (вбудований)

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

Ви можете спробувати це у кращому. NET регулярному тестері вираження .

Я використовував зразок джерела:

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

Це виявило збіг:

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

хоча насправді вийшло так:

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

Нарешті, мені дуже сподобалась стаття Джеффа Етвуда: Розбір Html The Cthulhu Way . Досить смішно, він наводить відповідь на це запитання, що наразі має понад 4 кб голосів.


18
System.Textне входить до складу C #. Це частина .NET.
Джон Сондерс

8
У першому рядку вашого regex ( (?=<ul\s*id="matchMe"\s*type="square"\s*>) # match start with <ul id="matchMe"...), між "<ul" та "id" має бути \s+, ні \s*, якщо ви не хочете, щоб він збігався <ulid = ...;)
C0deH4cker

@ C0deH4cker Ви правильні, вираз повинен мати \s+замість \s*.
Сем

4
Не те, що я насправді це розумію, але я думаю, що ваш <img src="images/pic.jpg" />
регекс

3
@Scheintod Дякую за коментар. Я оновив код. Попередній вираз не вдався до самозакриття тегів, які були /десь усередині, які не вдалися до вашого <img src="images/pic.jpg" />HTML.
Сем

258

Я пропоную використовувати QueryPath для розбору XML та HTML у PHP. Це в основному той самий синтаксис, що й jQuery, тільки він знаходиться на стороні сервера.


8
@ Kyle - jQuery не розбирає XML, він використовує вбудований аналізатор клієнта (якщо такий є). Тому для цього вам не потрібен jQuery, а лише два рядки простого старого JavaScript . Якщо вбудованого аналізатора немає, jQuery не допоможе.
RobG

1
@RobG Насправді jQuery використовує DOM, а не вбудований аналізатор.
Qix - МОНІКА ПОМИЛИ

11
@ Qix - тоді краще скажіть авторам документації: " jQuery.parseXML використовує вбудовану функцію розбору браузера ... ". Джерело: jQuery.parseXML ()
RobG

6
Прийшовши сюди з запитання про меми ( meta.stackexchange.com/questions/19478/the-many-memes-of-meta/… ), мені подобається, що одна з відповідей - «Використовувати jQuery»
Jorn,

221

Хоча відповіді, що ви не можете розібрати HTML за допомогою регулярних виразів, правильні, вони тут не застосовуються. OP просто хоче проаналізувати один тег HTML за допомогою регулярних виразів, і це те, що можна зробити з регулярним виразом.

Пропонований регулярний вираз помиляється, хоча:

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

Якщо ви щось то додати в регулярний вираз, з допомогою відкату може бути змушений відповідати дурні речі , як <a >>, [^/]занадто дозвільний. Також зауважте, що <space>*[^/]*це зайве, оскільки [^/]*може також відповідати пробілам.

Моя пропозиція була б

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

Де (?<! ... )(в регексах Перла) негативний огляд. Він читає "a <, то слово, то все, що не є>, останнім з яких може бути не a /, а потім>".

Зауважте, що це дозволяє такі речі <a/ >(як і оригінальний регулярний вираз), тому якщо ви хочете щось більш обмежувальне, вам потрібно побудувати регулярний вираз, щоб відповідати парам атрибутів, розділеним пробілами.


29
+1 за те, що зазначає, що питання не про розбір повного (X) HTML-коду, а про відповідність (X) HTML-відкритих тегів.
LarsH

10
Більшість відповідей, схоже, ігнорують, це те, що HTML-аналізатор може дуже добре використовувати регулярні вирази для своєї реалізації частин HTML, і я був би здивований, якби більшість аналізаторів цього не зробили.
Thayne

@Thayne Рівно. Під час розбору окремих тегів регулярний вираз є правильним інструментом для роботи. Досить смішно, що треба прокрутити половину сторінки, щоб знайти розумну відповідь. Прийнята відповідь невірна, оскільки вона змішує лексику та синтаксичний аналіз.
kasperd

2
Відповідь, наведена тут, не буде, коли значення атрибута містить символ '>' або '/'.
Мартін Л

Це буде неправильно працювати на HTML, що містить коментарі або розділи CData. Він також не працюватиме правильно, якщо в атрибуті, що цитується, міститься >символ. Я погоджуюся з тим, що запропонована ОП може бути виконана за допомогою регулярного вираження, але представлене тут далеко не просто.
ЖакБ

183

Спробуйте:

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

Це схоже на ваше, але останнє >не повинно бути після нахилу, а також приймає h1.


107
<a href="foo" title="5> 3 "> На жаль </a>
Гарет

21
Це дуже правда, і я думав про це, але припускав, що >символ належним чином перейшов до & gt ;.
Кобі

65
>є дійсним у значенні атрибута. Дійсно, у серіалізації 'canonical XML' ви не повинні використовувати &gt;. (Що не зовсім актуально, за винятком того, щоб підкреслити, що >значення атрибута зовсім не є незвичайною річчю.)
bobince

5
@Kobi: що означає знак оклику (той, котрий ви розмістили в кінці) у знаку regexp?
Марко Демайо

6
@bobince: ти впевнений? Я більше не розумію, так це і дійсний HTML:<div title="this tag is a <div></div>">hello</div>
Marco Demaio

179

Сунь Цзи, давньокитайський стратег, генерал і філософ, сказав:

Кажуть, що якщо ви знаєте своїх ворогів і знаєте себе, ви можете виграти сотню битв без жодної програші. Якщо ви знаєте лише себе, але не свого опонента, ви можете перемогти або програти. Якщо ви не знаєте ні себе, ні свого ворога, ви завжди будете наражати себе на небезпеку.

У цьому випадку ваш ворог - це HTML, і ви є самим собою або регулярним виразом. Ви навіть можете бути Perl з неправильним регулярним виразом. Знайте HTML. Пізнай себе.

Я склав хайку, що описує природу HTML.

HTML has
complexity exceeding
regular language.

Я також склав хайку, що описує природу виразки в Perl.

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

Вихід:

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

По суті, просто визначте назви вузлів елементів, які самостійно закриваються, завантажте весь HTML-рядок у бібліотеку DOM, захопіть усі елементи, пропустіть та відфільтруйте ті, які не самозакриваються та працюють над ними.

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


1
Якщо ви маєте справу з реальним XHTML, додайте до нього getElementsByTagName NSта вкажіть простір імен.
meder omuraliev

148

Я не знаю вашої точної потреби в цьому, але якщо ви також використовуєте .NET, не могли б ви використовувати Html Agility Pack ?

Витяг:

Це бібліотека коду .NET, яка дозволяє розбирати HTML-файли "з Інтернету". Синтаксичний синтаксичний аналізатор дуже толерантний до неправильного HTML.


137

Ви хочете, щоб перший, якому >не передував a /. Подивіться тут, щоб дізнатися, як це зробити. Це називається негативним поглядом позаду.

Однак, наївна реалізація цього результату призведе до відповідності <bar/></foo>в цьому прикладі документа

<foo><bar/></foo>

Чи можете ви надати трохи більше інформації про проблему, яку ви намагаєтеся вирішити? Ви ітератуєте теги програмно?


1
Так, я впевнена. Визначивши всі теги, які зараз відкриті, порівняйте їх із закритими тегами в окремому масиві. RegEx болить мій мозок.
Джефф

122

W3C пояснює розбір у формі псевдопоточної форми:
W3C Link

Дотримуйтесь Var посилання на QName, Sі , Attributeщоб отримати більш ясну картину.
Виходячи з цього, ви можете створити досить хороший регулярний малюнок для обробки речей, таких як зачистка тегів.


5
Це не форма pgeedo regexp, це форма EBNF, як зазначено тут: XML-специфікація, додаток 6
Rob G

106

Якщо вам це потрібно для PHP:

У PHP DOM функції не працюватимуть належним чином , якщо він не буде правильно відформатований XML. Незалежно від того, наскільки краще їх використання для решти людства.

simplehtmldom - це добре, але я виявив це трохи баггі, і він досить важкий для пам'яті [Збій на великих сторінках.]

Я ніколи не використовував querypath , тому не можу коментувати його корисність.

Ще один, щоб спробувати, це мій DOMParser, який дуже легкий на ресурсах, і я щасливо використовую деякий час. Простий у навчанні та потужний.

Для Python та Java були розміщені подібні посилання.

Для низових людей - я писав свій клас лише тоді, коли аналізатори XML виявились нездатними протистояти реальному використанню. Релігійна заборона просто запобігає опублікуванню корисних відповідей - будьте в курсі питання, будь ласка.


95

Ось рішення:

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

Щоб перевірити це глибоко, я вписав у рядок теги автоматичного закриття на зразок:

  1. <год />
  2. <br/>
  3. <br>

Я також вводив теги за допомогою:

  1. один атрибут
  2. більше ніж один атрибут
  3. атрибути, значення яких пов'язане або в одиничні, або в подвійні лапки
  4. атрибути, що містять одиничні лапки, коли роздільник є подвійною цитатою і навпаки
  5. "неприхотність" атрибути з пробілом перед символом "=", після нього і перед і після нього.

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

<EDIT> Я забув, що запитання від користувача полягало у тому, щоб уникнути розбору тегів, що самозакриваються. У цьому випадку візерунок простіший, перетворюючись на такий:

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

Користувач @ridgerunner зауважив, що шаблон не допускає атрибутів, які не котируються, або атрибутів без значення . У цьому випадку точна настройка приносить нам таку схему:

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

</EDIT>

Розуміння закономірності

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

  1. перший підвираз (\ w +) відповідає імені тегу
  2. другий підвираз містить шаблон атрибута. Він складається з:
    1. один або кілька пробілів \ s +
    2. назва атрибута (\ w +)
    3. нуль або більше пробілів \ s * (можна чи ні, залишаючи пробіли тут)
    4. символ "="
    5. знову нуль або більше пробілів
    6. роздільник значення атрибута, одинарний або подвійний лапок ('| "). У шаблоні одинарна цитата виходить з-за того, що вона збігається з роздільником рядка PHP. Цей підвираз зафіксовано в круглих дужках, щоб на нього можна було посилатися знову проаналізувати закриття атрибуту, тому це дуже важливо.
    7. значення атрибута, що відповідає майже будь-чому: (. *?); у цьому конкретному синтаксисі, використовуючи жадібну відповідність (знак питання після зірочки), двигун RegExp дозволяє оператору, подібному до "випередження", що відповідає нічого, крім того, що випливає з цього підвиразу
    8. тут приходить задоволення: частина \ 4 - це оператор зворотної референції , який посилається на суб-вираз, визначений раніше в шаблоні, в цьому випадку я маю на увазі четвертий підвираз, який є першим розмежувачем атрибутів, знайденим
    9. нуль або більше пробілів \ s *
    10. підвираз атрибута закінчується тут із зазначенням нуля або більше можливих випадків, заданих зірочкою.
  3. Тоді, оскільки тег може закінчуватися пробілом перед символом ">", нульовий або більше пробілів збігаються з підпатком \ s *.
  4. Тег, який повинен відповідати, може закінчуватися простим символом ">" або можливим закриттям XHTML, яке використовує косу рису перед ним: (/> |>). Косою косою рисою, звичайно, не вдалося, оскільки вона збігається з роздільником звичайного виразу.

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


12
Не відповідає чинним тегам , мають атрибути без значення, тобто <option selected>. Також не відповідає дійсним тегам з котируваними значеннями атрибутів, тобто <p id=10>.
ridgerunner

1
@ridgerunner: Дуже дякую за ваш коментар. У цьому випадку шаблон повинен трохи змінити: $ pattern = '/ <(\ w +) (\ s + (\ w +) (\ s * \ = \ s * (\' | "|) (. *?) \\ 5 \ s *)) * \ s *> / ', я перевірив це і працює в разі не котируються атрибутів або атрибутів без значення ?.
Emanuele Del Grande

Як щодо пробілу перед назвою тегу: < a href="http://wtf.org" >я впевнений, що він легальний, але ви не співпадаєте.
Флоріс

7
НЕ вибачте, пробіли перед іменем тегів є незаконними. Окрім того, що ви "досить впевнені", чому ви не надасте деякі докази свого заперечення? Ось мої, w3.org/TR/xml11/#sec-starttags, згадані в XML 1.1, і ви можете знайти те саме для HTML 4, 5 та XHTML, як перевірка W3C також попереджатиме, якщо ви зробите тест. Як і багато інших благ-поетів тут, я все ще не отримав жодної розумної аргументації, окрім сотень мінусів моїх відповідей, щоб продемонструвати, де мій код не відповідає за правилами контракту, зазначеними у питанні. Я б тільки їх вітав.
Емануель Дель Гранде

@ridgerunner звичайно ваш коментар був розумним та вітальним.
Емануель Дель Гранде

91

Щоразу, коли мені потрібно швидко витягнути щось із документа HTML, я використовую Tidy для перетворення його в XML, а потім використовую XPath або XSLT, щоб отримати те, що мені потрібно. У вашому випадку щось подібне:

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

89

Раніше я використовував інструмент з відкритим кодом під назвою HTMLParser . Він розроблений для розбору HTML різними способами і цілком служить цілі. Він може аналізувати HTML як різний тренод, і ви можете легко використовувати його API, щоб отримати атрибути з вузла. Перевірте це і подивіться, чи це може вам допомогти.


84

Мені подобається розбирати HTML з регулярними виразами. Я не намагаюся розібрати ідіотський HTML, який навмисно порушений. Цей код є моїм головним парсером (видання Perl):

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

Він називається htmlsplit, розбиває HTML на рядки, по одному тегу або фрагменту тексту в кожному рядку. Потім рядки можна додатково обробити іншими текстовими інструментами та сценаріями, такими як grep , sed , Perl тощо. Я навіть не жартую :) Насолоджуйтесь.

Це досить просто, щоб перевлаштувати мій сценарій Perl-першочерговий Perl-сценарій в хорошу потокову річ, якщо ви хочете обробити величезні веб-сторінки. Але це насправді не потрібно.

Б'юсь об заклад, що мене за це відмовлять.

Спліт HTML


Незважаючи на моє сподівання, це отримало кілька результатів, тому я запропоную кілька кращих регулярних виразів:

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

Вони хороші для XML / XHTML.

З незначними варіаціями він може впоратися з безладним HTML ... або перетворити спочатку HTML -> XHTML.


Найкращий спосіб писати регулярні вирази - це в стилі Lex / Yacc , не як непрозорі однолінійки або коментовані багаторядкові чудовиська. Я ще цього не робив; ці ледве це потребують.


35
"Я не намагаюся розібрати ідіотський HTML, який навмисно порушений." Як ваш код знає різницю?
Кевін Панько

Ну це не має великого значення, зламаний чи ні HTML. Річ все одно розділить HTML на теги та текст. Єдине, що може зіпсувати це, якщо люди містять у тексті чи атрибутах символи без розміру <або>. На практиці мій крихітний спліттер HTML працює добре. Мені не потрібна величезна чудовисько, яка є евристикою. Прості рішення не для всіх ...!
Сем Уоткінс

Для XML / XHTML я додав кілька більш простих регулярних виразів для вилучення тегів, тексту та атрибутів.
Сем Уоткінс

(отримати атрибути помилка 1) /(\w+)="(.*?)"/передбачає подвійні лапки. Він буде пропускати значення в одиничних лапках. У версії html версії 4 та більш раннього значення дозволено без котирування, якщо це просте слово.
Девід Андерссон

(отримати атрибути помилка 2) /(\w+)="(.*?)"/може помилково збігатися з текстом, схожим на атрибут у межах атрибута, наприклад <img title="Nope down='up' for aussies" src="..." />. Якщо застосовуватись у всьому світі, він також відповідатиме цим нормам у звичайному тексті чи у коментарях html.
Девід Андерссон

74

Ось аналізатор на основі PHP, який аналізує HTML, використовуючи деякий нечестивий регулярний вираз. Як автор цього проекту, я можу вам сказати, що можна проаналізувати HTML за допомогою регулярного вираження, але не ефективно. Якщо вам потрібне серверне рішення (як я зробив для мого плагіна WP-Typography WordPress ), це працює.


1
htmlawed - це ще один PHP-проект, який аналізує HTML для фільтрації, перетворення тощо.
користувач594694

Ні, ви не можете розібрати HTML з допомогою регулярного вираження. Але для деяких підмножин це може спрацювати.
mirabilos

71

Є деякі хороші регулярні вирази для заміни HTML з BBCode тут . Зауважте, що він не намагається повністю проаналізувати HTML, а лише переосмислити його. Ймовірно, він може дозволити собі вбити теги, які його простий "аналізатор" не може зрозуміти.

Наприклад:

$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
Не робіть цього. Будь ласка.
малетор

68

Що стосується методу RegExp для розбору (x) HTML, відповідь усіх тих, хто говорив про певні межі: ви недостатньо навчені управляти силою цієї потужної зброї, оскільки НІХТО тут не говорило про рекурсію .

Колега з RegExp-агностика повідомив мені про цю дискусію, яка, безумовно, не є першою в Інтернеті щодо цієї старої та гарячої теми.

Прочитавши кілька публікацій, перше, що я зробив, було шукати рядок "? R" у цій темі. Другий - пошук щодо "рекурсії".
Ні, свята корова, сірника не знайдено.
Оскільки головний механізм, на який будується аналізатор, ніхто не згадав, я незабаром усвідомив, що ніхто не розуміє.

Якщо для (x) HTML-аналізатора потрібна рекурсія, для розбору RegExp без рекурсії недостатньо для цієї мети. Це проста конструкція.

Чорне мистецтво RegExp важко освоїти , тому , можливо , існують додаткові можливості , ми залишили в той час пробувати і відчувати наше особисте рішення , щоб захопити весь Інтернет в одній руці ... Ну, я впевнений , що про це :)

Ось магічна картина:

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

Просто спробуйте.
Він написаний у вигляді рядка PHP, тому модифікатор "s" змушує класи включати нові рядки.
Ось зразок примітки до посібника PHP, який я написав у січні: Довідка

(Будьте уважні, у цій примітці я неправильно використав модифікатор "m"; його слід стерти, незважаючи на те, що він відкидається двигуном RegExp, оскільки не використовувався жоден ^ або $ кріплення).

Тепер ми могли б говорити про межі цього методу з більш обізнаної точки зору:

  1. відповідно до конкретної реалізації двигуна RegExp, рекурсія може мати обмеження в кількості вкладених шаблонів, проаналізованих , але це залежить від мови, що використовується
  2. хоча пошкоджений (x) HTML не призводить до серйозних помилок, він не санізований .

Так чи інакше, це лише шаблон RegExp, але він розкриває можливість розробити безліч потужних реалізацій.
Я написав цю схему, щоб використовувати рекурсивний аналізатор спуску двигуна шаблонів, який я створив у своєму рамках, і продуктивність справді чудова, як у часи виконання, так і у використанні пам'яті (нічого спільного з іншими двигунами шаблонів, які використовують той самий синтаксис).


35
Я покладу це в бін "Regex, який не дозволяє більше, ніж в атрибутах". Перевірте це на <вхідне значення = "5> 3?" />
Гарет

68
Якщо ви помістите щось подібне до виробничого коду, вас, швидше за все, зняв би сервіс. Присяжні ніколи його не засудять.
aehiilrs

30
Регулярні вирази не можуть працювати, оскільки за визначенням вони не є рекурсивними. Додавання рекурсивного оператора до регулярних виразів в основному робить CFG лише з біднішим синтаксисом. Чому б не використовувати в першу чергу щось, призначене для рекурсії, а не насильницьку рекурсію в щось вже переповнене сторонніми функціоналами?
Welbog

16
Моє заперечення не є однією з функціональних можливостей, це одноразово вкладене. Проблема RegEx полягає в тому, що до моменту розміщення маленьких лайнерів cutsey з'являється, що ви зробили щось більш ефективно ("Дивіться один рядок коду!"). І звичайно, ніхто не згадує півгодини (або 3), які вони провели зі своїм шпаргалкою та (сподіваємось), перевіряючи всі можливі перестановки введення. І як тільки ви пройдете все це, коли технічний супровід розібрається або підтвердить код, вони не зможуть просто подивитися на нього і побачити, що це правильно.
Доводиться розбирати

15
... знати, що це добре. І це станеться навіть з людьми, які добре підходять до регулярного виразу. І, чесно кажучи, я підозрюю, що переважна більшість людей цього не буде добре знати. Отже, ви берете один з найвідоміших кошмарів технічного обслуговування і поєднуєте його з рекурсією, яка є іншим кошмаром технічного обслуговування, і я думаю собі, що мені справді потрібно в моєму проекті - це хтось менш розумний. Мета - написати код, який погані програмісти можуть підтримувати, не порушуючи кодову базу. Я знаю, що це дозволяє кодувати до найменш поширеного знаменника. Але найняти відмінний талант важко, і ти часто ...
Ооранг

62

Як вже вказували багато людей, HTML не є звичайною мовою, яка може зробити його дуже важким для розбору. Моє рішення для цього - перетворити його на звичайну мову за допомогою охайної програми, а потім використовувати XML-аналізатор для споживання результатів. Для цього існує маса хороших варіантів. Моя програма написана за допомогою Java з бібліотекою jtidy, щоб перетворити HTML в XML, а потім Jaxen в xpath в результат.


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

Частини пояснили:

<: вихідний символ

\s*: він може мати пробіли перед назвою тегу (некрасиво, але можливо).

(\w+): теги можуть містити літери та цифри (h1). Добре, \wтакож відповідає '_', але це не шкодить, я думаю. Якщо цікаво використовувати натомість ([a-zA-Z0-9] +).

[^/>]*: нічого, крім >і /до закриття>

>: закриття >

НЕЗАЄМО

І тим, хто недооцінює регулярні вислови, говорячи, що вони настільки ж сильні, як і звичайні мови:

a n ba n ba n, який не є регулярним і навіть не контекстним, може відповідати^(a+)b\1b\1$

Зворотний довід FTW !


@GlitchMr, це було його суть. Сучасні регулярні вирази технічно не є регулярними, а також немає жодних причин для них.
alanaktion

3
@alanaktion: "Сучасні" регулярні вирази (читати: з розширеннями Perl) не можуть збігатися в межах O(MN)(M - звичайна довжина виразу, N - довжина тексту). Зворотній зв'язок - одна з причин цього. Реалізація у awk не має зворотних посилань і відповідає всім у O(MN)строк.
Конрад Боровський

56

Якщо ви просто намагаєтеся знайти ці теги (без амбіцій розбору), спробуйте цей регулярний вираз:

/<[^/]*?>/g

Я написав це за 30 секунд і перевірив тут: http://gskinner.com/RegExr/

Він відповідає типам згаданих вами тегів, ігноруючи типи, які ви сказали, що хочете ігнорувати.


2
Я думаю, ти маєш на увазі \/>замість цього \\>.
Джастін Морган

Ні, просто \>те, що я мав на увазі; Я ніколи не мав на увазі редагувати регулярне висловлювання свого початкового повідомлення.
Lonnie Best

2
FYI, вам не потрібно уникати кутових дужок. Звичайно, уникнути їх все одно не шкода, але подивіться на плутанину, якої ви могли уникнути. ;)
Алан Мур

Я інколи уникаю зайвого, коли не впевнений, чи є щось особливе чи ні. Я відредагував відповідь; це працює так само, але більш стисло.
Lonnie Best

Дивлячись на це зараз, я не знаю, чому я вважав, що ви це маєте на увазі \/, оскільки це зробить абсолютно протилежне вимогам. Можливо, я подумав, що ви пропонуєте негативну схему фільтру.
Джастін Морган

54

Мені здається, ви намагаєтеся зрівняти теги без "/" наприкінці. Спробуйте це:

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

8
Це не працює. Для введення '<xa = "<b>" /> <y>' збіги є x і y, хоча x закінчується.
закінчення

51

Це правда, що при програмуванні зазвичай краще використовувати виділені парсери та API замість регулярних виразів при роботі з HTML, особливо якщо точність є першочерговою (наприклад, якщо обробка може мати наслідки для безпеки). Однак я не приписую догматичному погляду, що розмітка в стилі XML ніколи не повинна оброблятися регулярними виразами. Бувають випадки, коли регулярні вирази - чудовий інструмент для роботи, наприклад, коли ви робите одноразові редагування в текстовому редакторі, виправляєте зламані файли XML або працюєте з форматами файлів, схожими, але не зовсім XML. Необхідно пам’ятати про деякі проблеми, але вони не є непереборними або навіть обов’язково актуальними.

Простий регулярний вираз, як <([^>"']|"[^"]*"|'[^']*')*>правило, досить хороший у таких випадках, як ті, про які я щойно згадував. Це все є наївним рішенням, але воно дійсно допускає невкодовані >символи у значеннях атрибутів. Якщо ви шукаєте, наприклад, tableтег, ви можете його адаптувати як </?table\b([^>"']|"[^"]*"|'[^']*')*>.

Для того, щоб зрозуміти, як виглядатиме «просунутіший» HTML-регулярний вираз, наступне виконує досить поважну роботу з імітації поведінки браузера в реальному світі та алгоритму розбору HTML5:

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

Далі відповідає досить чітке визначення тегів XML (хоча воно не враховує повний набір символів Unicode, дозволений у іменах XML):

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

Зрозуміло, вони не враховують оточуючий контекст та кілька крайових випадків, але навіть з такими речами можна було б вирішитись, якщо ви дійсно цього хотіли (наприклад, шляхом пошуку між відповідниками іншого регексу).

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


49

Хоча для цієї мети не підходить і ефективно використовувати регулярні вирази, іноді регулярні вирази забезпечують швидке вирішення проблем простої відповідності, і на мою думку, не регулярно використовувати регулярні вирази для тривіальних творів.

Існує остаточна публікація в блозі про відповідність найпотаємніших елементів HTML, яку написав Стівен Левітан.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.