RegEx соответствует открытым тегам, кроме автономных тегов XHTML


1475

Мне нужно сопоставить все эти открывающие теги:

<p>
<a href="foo">

Но не эти:

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

Я придумал это и хотел убедиться, что я правильно понял. Я только захватил a-z.

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

Я верю, что это говорит:

  • Найти меньше, чем
  • Найдите (и запишите) az один или несколько раз, затем
  • Найдите ноль или более пробелов, затем
  • Найти любой символ ноль или более раз, жадный, кроме /, затем
  • Найти больше, чем

У меня есть это право? И что более важно, что вы думаете?

Ответы:


4417

Вы не можете разобрать [X] HTML с регулярным выражением. Потому что HTML не может быть проанализирован с помощью регулярных выражений. Regex - это не инструмент, который можно использовать для правильного разбора HTML. Как я уже много раз отвечал на вопросы HTML-и-регулярных выражений здесь, использование регулярных выражений не позволит вам использовать HTML. Регулярные выражения - это инструмент, который недостаточно сложен для понимания конструкций, используемых HTML. HTML не является регулярным языком и, следовательно, не может быть проанализирован с помощью регулярных выражений. Запросы Regex не имеют возможности разбивать HTML на его значимые части. так много раз, но это не до меня. Даже расширенные нерегулярные регулярные выражения, используемые Perl, не справляются с задачей анализа HTML. Ты никогда не заставишь меня взломать. HTML - это язык достаточной сложности, который не может быть проанализирован регулярными выражениями. Даже Джон Скит не может анализировать HTML с помощью регулярных выражений. Каждый раз, когда вы пытаетесь разобрать HTML с помощью регулярных выражений, нечестивый ребенок плачет кровью девственниц, а русские хакеры набивают ваше веб-приложение. Разбор HTML с помощью регулярных выражений вызывает испорченные души в мир живых. HTML и регулярные выражения идут вместе, как любовь, брак и ритуальное детоубийство. <Центр> не может удержать это слишком поздно. Сила регулярных выражений и HTML в одном и том же концептуальном пространстве разрушит ваш разум, словно водянистая замазка. Если вы анализируете HTML с помощью регулярных выражений, вы уступаете им и их кощунственным путям, которые обрекают нас всех на нечеловеческий труд для Того, чье Имя не может быть выражено на Основном Многоязычном Плане, он приходит. HTML-plus-regexp будет разжижать нервы чувствующего, пока вы наблюдаете, как ваша психика увядает от ужаса.слишком поздно, слишком поздно, мы не можем быть спасены, так как перегрузка ребенка гарантирует, что регулярное выражение поглотит все живые ткани (за исключением HTML, которого оно не может, как предсказывалось ранее), дорогой лорд, помогите нам, как можно выжить в этом бедствии, используя регулярное выражение для анализа HTML обречен человечеству на вечность ужасных пыток и дыр в безопасности, используя rege x в качестве инструмента для обработки HTML, устанавливает разрыв между этим миром и ужасной сферой из-за ошибочных сущностей (таких как сущности SGML, но более коррумпированных), просто проблески множества мир рег бывшие анализаторы для 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
Если вы не видите этот пост, вот его скриншот
Эндрю Китон,

3251

Хотя произвольный HTML с одним регулярным выражением невозможен, иногда целесообразно использовать его для анализа ограниченного, известного набора HTML.

Если у вас есть небольшой набор HTML-страниц, из которых вы хотите очистить данные и затем поместить их в базу данных, регулярные выражения могут работать нормально. Например, недавно я хотел получить имена, партии и округа федеральных представителей Австралии, которые я получил с веб-сайта парламента. Это была ограниченная разовая работа.

Regexes отлично работали для меня, и их очень быстро настраивали.


131
Кроме того, очистка достаточно регулярно отформатированных данных из больших документов будет намного быстрее при разумном использовании scan & regex, чем при использовании любого универсального синтаксического анализатора. И если вы знакомы с регулярными выражениями при кодировании, код будет быстрее, чем при кодировании xpath. И почти наверняка менее хрупкий к изменениям в том, что вы чистите. Так бле
Майкл Джонстон

255
@MichaelJohnston "Менее хрупкий"? Почти наверняка нет. Регулярные выражения заботятся о деталях форматирования текста, которые анализатор XML может игнорировать. Переключение между &foo;кодировками и CDATAразделами? Использование HTML Minifier для удаления всех пробелов в документе, которые не отображаются в браузере? Синтаксический анализатор XML не будет заботиться, равно как и хорошо написанный оператор XPath. Основанный на регулярных выражениях "парсер", с другой стороны ...
Чарльз Даффи

41
@CharlesDuffy для одноразовой работы это нормально, а для пробелов мы используем \ s +
квант

68
@ xiaomao, действительно, если мне нужно знать все хитрости и обходные пути, чтобы получить 80% -ное решение, которое в остальное время не работает, «работает для вас», я не могу вас остановить. Тем временем я нахожусь на своей стороне забора, используя парсеры, которые работают на 100% синтаксически корректного XML.
Чарльз Даффи

374
Когда-то мне приходилось извлекать некоторые данные с ~ 10 тыс. Страниц, все с одним и тем же шаблоном HTML. Они были завалены ошибками HTML, из-за которых парсеры задыхались, и все их стили были встроенными или с <font>т. Д .: без классов или идентификаторов, помогающих ориентироваться в DOM. После целого дня борьбы с «правильным» подходом я, наконец, переключился на решение регулярных выражений, и оно заработало через час.
Пол А Jungwirth

2039

Я думаю, что недостаток в том, что HTML - это грамматика Chomsky Type 2 (контекстно-свободная грамматика), а RegEx - это грамматика Chomsky Type 3 (обычная грамматика) . Поскольку грамматика типа 2 существенно сложнее, чем грамматика типа 3 (см. Иерархию Хомского ), математически невозможно проанализировать XML с помощью RegEx.

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


226
ОП просит проанализировать очень ограниченное подмножество XHTML: начальные теги. То, что делает (X) HTML CFG, это его потенциальная возможность иметь элементы между начальным и конечным тегами других элементов (как в грамматическом правиле A -> s A e). (X) HTML не имеет этого свойства в стартовом теге: стартовый тег не может содержать другие стартовые теги. Подмножество, которое OP пытается проанализировать, не является CFG.
LarsH

101
В теории CS регулярные языки являются строгим подмножеством языков без контекста, но реализации регулярных выражений в основных языках программирования являются более мощными. Как описывает noulakaz.net/weblog/2007/03/18/… , так называемые «регулярные выражения» могут проверять простые числа в унарном, что, безусловно, не может сделать регулярное выражение из теории CS.
Адам Михалчин

11
@eyelidlessness: то же самое «только если» применяется ко всем CFG, не так ли? Т.е., если (X) ввод HTML не является правильно сформированным, даже полноценный парсер XML не будет работать надежно. Возможно, если вы приведете примеры «ошибок синтаксиса HTML, реализованных в реальных пользовательских агентах», на которые вы ссылаетесь, я пойму, к чему вы стремитесь лучше.
LarsH

82
@AdamMihalcin совершенно прав. Большинство существующих движков регулярных выражений более мощные, чем грамматики Хомского типа 3 (например, сопоставление без жадности, обратные ссылки). Некоторые движки регулярных выражений (например, Perl) завершены по Тьюрингу. Это правда, что даже это плохие инструменты для анализа HTML, но этот часто цитируемый аргумент не является причиной, почему.
dubiousjim

27
Это самый «полный и короткий» ответ здесь. Это заставляет людей изучать основы формальной грамматики и языков и, надеюсь, немного математики, чтобы они не тратили время на безнадежные вещи, такие как решение NP-задач за полиномиальное время
Мишмашру

1333

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

  1. Решите проблему остановки.
  2. Квадратный круг.
  3. Разрешите задачу коммивояжера в O (log n) или меньше. Если это будет больше, вам не хватит оперативной памяти, и двигатель зависнет.
  4. Шаблон будет довольно большим, поэтому убедитесь, что у вас есть алгоритм, который без потерь сжимает случайные данные.
  5. Почти там - просто делим все это на ноль. Очень просто.

Я еще не совсем закончил последнюю часть, но я знаю, что приближаюсь. По CthulhuRlyehWgahnaglFhtagnExceptionкакой-то причине он продолжает выдавать s, поэтому я собираюсь перенести его на VB 6 и использовать On Error Resume Next. Я обновлю код, когда буду исследовать эту странную дверь, которая только что открылась в стене. Хм.

PS Пьер де Ферма также придумал, как это сделать, но поля, на котором он писал, было недостаточно для кода.


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

148
Небольшая проблема Ферма была решена с помощью мягких полей в современном программном обеспечении для редактирования текста.
kd4ttc

50
Проблема небольшого поля Ферма была решена Рэндаллом Манро, установив нулевой размер
шрифта

29
К вашему сведению: проблема Ферма была фактически решена в 1995 году , и математикам потребовалось всего 358 лет, чтобы это сделать.
jmiserez

10
Я смог обойти этот липкий шаг деления на ноль, вместо этого используя броуновские трещотки, полученные из холодного синтеза ... хотя это работает только тогда, когда я удаляю космологическую постоянную.
Тим Ленер

1073

Отказ от ответственности : используйте парсер, если у вас есть возможность. Это сказал ...

Это регулярное выражение, которое я использую (!) Для соответствия тегам HTML:

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

Возможно, он не идеален, но я пробежал этот код по большому количеству HTML. Обратите внимание, что он даже ловит странные вещи, такие как <a name="badgenerator"">, которые появляются в Интернете.

Я полагаю, что для того, чтобы он не совпадал с самодостаточными тегами, вы можете использовать отрицательный взгляд Коби :

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

или просто объединить, если и если нет.

Для downvoters: это рабочий код из реального продукта. Я сомневаюсь, что у любого, кто читает эту страницу, будет впечатление, что в HTML приемлемо использовать регулярные выражения.

Предостережение : я должен отметить, что это регулярное выражение все еще не работает при наличии блоков CDATA, комментариев, элементов сценария и стиля. Хорошая новость в том, что вы можете избавиться от тех, кто использует регулярные выражения ...


95
Я бы с чем - то , что работает на здравых вещей , чем плакать о не универсально идеально :-)
prajeesh Кумар

55
Кто-то использует CDATA внутри HTML?
Дунайский моряк

16
так что вы на самом деле не решаете проблему синтаксического анализа только с помощью регулярного выражения, но как часть синтаксического анализатора это может работать. PS: рабочий продукт не означает хороший код. Без обид, но именно так работает промышленное программирование и получает свои деньги
Мишмашру

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

2
если мы просто пытаемся сопоставить и не соответствовать приведенным примерам, /<.([^r>][^>]*)?>/g работает :-) // javascript: '<p> <a href = "foo"> <br /> <hr class = "foo" />'.match(/<.([^r>][^>]*)?>/g)
imma

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.

Ох ... если вы хотите исходный код регулярного выражения, с некоторыми вспомогательными методами:

регулярное выражение для токенизации XML или полный обычный регулярное выражение


68
Господи, это массивно. Мой самый большой вопрос - почему? Вы понимаете, что все современные языки имеют парсеры XML, верно? Вы можете сделать все это в 3 строки и быть уверенным, что это сработает. Кроме того, вы также понимаете, что чистое регулярное выражение доказуемо неспособно делать определенные вещи? Если вы не создали гибридный анализатор регулярных выражений и императивного кода, но это не похоже на то, что вы сделали. Вы можете также сжать случайные данные?
Джастин Морган

113
@ Джастин, мне не нужна причина. Это могло быть сделано (и это не было незаконно / аморально), поэтому я сделал это. Уму нет никаких ограничений, кроме тех, которые мы признаем (Наполеон Хилл) ... Современные языки могут анализировать XML? В самом деле? И я подумал, что это незаконно! :-)
xanatos

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

31
@Justin Итак, парсер Xml по определению не содержит ошибок, а Regex - нет? Потому что, если Xml Parser по определению не содержит ошибок, может существовать xml, приводящий к его падению, и мы возвращаемся к шагу 0. Допустим, что и Xml Parser, и этот Regex пытаются проанализировать все "XML. Они МОГУТ анализировать некоторые «нелегальные» XML. Ошибки могут разбить их обоих. C # XmlReader, безусловно, более протестирован, чем этот Regex.
Ксанатос

31
Нет, нет ошибок без ошибок: 1) Все программы содержат как минимум одну ошибку. 2) Все программы содержат как минимум одну строку ненужного исходного кода. 3) Используя # 1 и # 2 и используя логическую индукцию, легко доказать, что любая программа может быть сведена к одной строке кода с ошибкой. (Из изучения Perl)
Скотт Уивер

299

В оболочке вы можете анализировать HTML используя sed :

  1. Turing.sed
  2. Написать HTML-парсер (домашнее задание)
  3. ???
  4. Прибыль!

Связано (почему вы не должны использовать регулярное выражение):


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

1
Нет, @Abdul. Это полностью, доказуемо (в математическом смысле) невозможно.
Palec

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

2
И, между прочим, шутка, на которую я ссылался, заключалась в содержании этого ответа до (радикальных) правок Кенорба, в частности, редакции 4, @Abdul.
Палек

3
Самое смешное, что OP никогда не просил разобрать html с помощью регулярных выражений. Он попросил сопоставить текст (который является HTML) с помощью регулярных выражений. Что вполне разумно.
Паралиф

274

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

На самом деле у Microsoft есть раздел Best Practices для регулярных выражений в .NET Framework, и он конкретно говорит о рассмотрении источника ввода .

Регулярные выражения имеют ограничения, но рассматривали ли вы следующее?

Платформа .NET уникальна в том, что касается регулярных выражений, поскольку она поддерживает определения балансирующих групп .

По этой причине я считаю, что вы МОЖЕТЕ проанализировать XML с помощью регулярных выражений. Однако обратите внимание, что это должен быть действительный XML ( браузеры очень просты для HTML и допускают неверный синтаксис XML внутри HTML ). Это возможно, поскольку «Определение балансирующей группы» позволит механизму регулярных выражений действовать в качестве КПК.

Цитата из статьи 1, приведенной выше:

Механизм регулярных выражений .NET

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

  • (?<group>) - помещает полученный результат в стек захвата вместе с группой имен.
  • (?<-group>) - выскакивает самый верхний захват с группой имен из стека захвата.
  • (?(group)yes|no) - соответствует части «да», если существует группа с именем группы, в противном случае не соответствует ни одной части.

Эти конструкции позволяют регулярному выражению .NET эмулировать ограниченный КПК, по существу позволяя простые версии операций стека: push, pop и empty. Простые операции в значительной степени эквивалентны увеличению, уменьшению и сравнению с нулем соответственно. Это позволяет механизму регулярных выражений .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

Вы можете попробовать это в A Better .NET Regular Expression Tester .

Я использовал образец источника:

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

Наконец, мне очень понравилась статья Джеффа Этвуда: Parsing Html The Cthulhu Way . Достаточно забавно, он цитирует ответ на этот вопрос, который в настоящее время имеет более 4 тысяч голосов.


18
System.Textне является частью C #. Это часть .NET.
Джон Сондерс

8
В первой строке вашего регулярного выражения ( (?=<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" />
Scheintod

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 - МОНИКА ПРОИЗОШЛА 22.09.14

11
@ Qix - вам лучше сказать авторам документации: « jQuery.parseXML использует встроенную функцию синтаксического анализа браузера… ». Источник: jQuery.parseXML ()
RobG

6
Придя сюда из вопроса о меме ( meta.stackexchange.com/questions/19478/the-many-memes-of-meta/… ), мне нравится, что один из ответов - «Использовать jQuery»
Jorn

221

Хотя ответы, которые вы не можете проанализировать с помощью регулярных выражений, верны, они здесь не применимы. OP просто хочет проанализировать один HTML-тег с регулярными выражениями, и это можно сделать с помощью регулярного выражения.

Предлагаемое регулярное выражение неверно, хотя:

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

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

Мое предложение будет

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

Где (?<! ... )(в регулярных выражениях Perl) отрицательный взгляд. Он читает «a <, затем слово, затем все, что не является>, последний из которых не может быть /, а затем>».

Обратите внимание, что это допускает такие вещи, как <a/ >(так же, как и в исходном регулярном выражении), поэтому, если вы хотите что-то более ограничительное, вам нужно создать регулярное выражение для сопоставления пар атрибутов, разделенных пробелами.


29
+1 за то, что отметили, что вопрос не в разборе полного (X) HTML, а в сопоставлении (X) открытых тегов HTML.
LarsH

10
Что-то еще, что большинство ответов, кажется, игнорирует, - то, что анализатор HTML может очень хорошо использовать регулярные выражения в его реализации для частей HTML, и я был бы удивлен, если бы большинство анализаторов не делали этого
Тейн

@ Тэйн Точно. При разборе отдельных тегов регулярное выражение является правильным инструментом для работы. Весьма смешно, что нужно прокручивать страницу наполовину, чтобы найти разумный ответ. Принятый ответ неверен, потому что он смешивает лексизм и синтаксический анализ.
Касперд

2
Ответ, данный здесь, потерпит неудачу, когда значение атрибута будет содержать символ «>» или «/».
Мартин Л

Это будет работать некорректно на HTML, содержащем комментарии или разделы CData. Это также не будет работать правильно, если цитируемый атрибут содержит >символ. Я согласен, что предложение OP может быть выполнено с помощью регулярного выражения, но представленное здесь далеко не упрощенное.
JacquesB

183

Пытаться:

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

Он похож на ваш, но последний >не должен быть после косой черты, а также принимает h1.


107
<a href="foo" title="5> 3 "> К сожалению </a>
Гарет

21
Это очень верно, и я подумал об этом, но я предположил, что >символ правильно экранирован на & gt ;.
Коби

65
>допустимо в значении атрибута. Действительно, в сериализации «канонический XML» вы не должны использовать &gt;. (Что не совсем уместно, за исключением того, чтобы подчеркнуть, что >в значении атрибута нет ничего необычного.)
bobince

5
@ Коби: что означает в регулярном выражении восклицательный знак (тот, который вы поставили перед концом)?
Марко Демайо

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

179

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

Говорят, что если вы знаете своих врагов и знаете себя, вы можете выиграть сотню сражений без единой потери. Если вы знаете только себя, но не своего противника, вы можете выиграть или проиграть. Если вы не знаете ни себя, ни своего врага, вы всегда будете подвергать опасности себя.

В этом случае ваш враг - это HTML, а вы либо сами, либо regex. Вы можете даже быть 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

Вы хотите, чтобы первый >не предшествовал /. Посмотрите здесь для деталей о том, как это сделать. Это называется негативным взглядом сзади.

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

<foo><bar/></foo>

Можете ли вы предоставить немного больше информации о проблеме, которую вы пытаетесь решить? Вы итерируете теги программно?


1
Да, я уверен. Определив все открытые теги, сравните их с закрытыми тегами в отдельном массиве. RegEx вредит моему мозгу.
Джефф

122

W3C объясняет синтаксический анализ в форме псевдо-регулярного выражения:
W3C Link

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


5
Это не форма регулярного выражения psuedo, это форма EBNF, как указано здесь: спецификация XML, приложение 6
Rob G

106

Если вам это нужно для PHP:

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

simplehtmldom - это хорошо, но я обнаружил, что он немного глючит, и это довольно много памяти [вылетает на больших страницах.]

Я никогда не использовал querypath , поэтому не могу комментировать его полезность.

Еще один пример - мой DOMParser, который очень лёгок на ресурсах, и какое-то время я с удовольствием его использовал. Простой в освоении и мощный.

Для Python и Java были размещены похожие ссылки.

Для downvoters - я написал свой класс только тогда, когда парсеры 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. Атрибуты «unpretty» с пробелом перед символом «=», после него и до и после него.

Если вы найдете что-то, что не работает в доказательстве концепции выше, я могу проанализировать код, чтобы улучшить мои навыки.

<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 как другой treenode, и вы можете легко использовать его 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-скрипт slurp-everything-first в хорошую потоковую вещь, если вы хотите обрабатывать огромные веб-страницы. Но это не совсем необходимо.

Могу поспорить, что за это проголосуют.

HTML Split


Вопреки моим ожиданиям это вызвало некоторые положительные отзывы, поэтому я предложу несколько лучших регулярных выражений:

/(<.*?>|[^<]+)\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 для фильтрации, конвертации и т. д. Имеет хороший код, если вы можете это понять!
user594694

Нет, вы не можете разобрать HTML с регулярным выражением. Но для некоторых подмножеств это может работать.
Мирабилось

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
Не делай этого. Пожалуйста.
maletor

68

На вопрос о методах RegExp для синтаксического анализа (x) HTML ответ на все те, кто говорил о некоторых ограничениях: вы недостаточно обучены, чтобы управлять силой этого мощного оружия, так как здесь никто не говорил о рекурсии .

Коллега по RegExp-агностику уведомил меня об этом обсуждении, которое, безусловно, не является первым в Интернете по этой старой и горячей теме.

После прочтения некоторых постов первым делом я искал строку «? R» в этой теме. Вторым был поиск по поводу «рекурсии».
Нет, святая корова, совпадений не найдено.
Поскольку никто не упомянул основной механизм, на котором построен синтаксический анализатор, я вскоре осознал, что никто не понял этого.

Если парсер (x) HTML нуждается в рекурсии, то для этой цели недостаточно парсера RegExp без рекурсии. Это простая конструкция.

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

Вот волшебный узор:

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

Просто попробуйте.
Он написан в виде строки PHP, поэтому модификатор "s" делает классы включающими символы новой строки.
Вот пример примечание на PHP руководство я написал января: Ссылка

(Будьте осторожны, в этой заметке я ошибочно использовал модификатор «m»; его следует стереть, несмотря на то, что он сбрасывается механизмом RegExp, так как не было использовано ^ или $ anchorage).

Теперь мы можем говорить об ограничениях этого метода с более осознанной точки зрения:

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

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


35
Я помещу это в «Regex, который не допускает больше, чем в атрибутах». Проверьте это по <input value = "is 5> 3?" />
Гарет

68
Если вы добавите что-то подобное в производственный код, вас, скорее всего, застрелит сопровождающий. Жюри никогда не осудит его.
aehiilrs

30
Регулярные выражения не могут работать, потому что по определению они не являются рекурсивными. Добавление рекурсивного оператора к регулярным выражениям в основном делает CFG только с худшим синтаксисом. Почему бы не использовать что-то, предназначенное для того, чтобы быть в первую очередь рекурсивным, а не насильственно вставлять рекурсию во что-то, уже переполненное посторонней функциональностью?
Welbog

16
Мое возражение - не функциональность, а время. Проблема с RegEx заключается в том, что к тому времени, когда вы публикуете небольшие однострочники, кажется, что вы сделали что-то более эффективно («Смотрите одну строчку кода!»). И, конечно, никто не упоминает о получасе (или 3), который они потратили на свою шпаргалку и (мы надеемся) тестирование каждой возможной перестановки ввода. И как только вы пройдете через все это, когда сопровождающий начнет выяснять или проверять код, он не сможет просто посмотреть на него и увидеть, что он прав.
Нужно

15
... знать, что это хорошо. И это произойдет даже с людьми, которые хорошо с регулярным выражением. И, честно говоря, я подозреваю, что подавляющее большинство людей не будут это хорошо знать. Таким образом, вы берете один из самых печально известных кошмаров обслуживания и комбинируете его с рекурсией, который является другим кошмаром обслуживания, и я думаю, что мне действительно нужно в моем проекте кого-то чуть менее умного. Цель состоит в том, чтобы написать код, который плохие программисты могут поддерживать, не нарушая основы кода. Я знаю, что все равно, что кодировать наименее общий знаменатель. Но нанять отличный талант сложно, и вы часто ...
Oorang

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
Я думаю, что вы имеете в виду \/>вместо \\>.
Джастин Морган

Нет, именно \>это я и имел ввиду; Я никогда не хотел редактировать регулярное выражение моего исходного поста.
Лонни Бест

2
К вашему сведению, вам не нужно избегать угловых скобок. Конечно, избегать их в любом случае не вредно, но посмотрите на путаницу, которой вы могли бы избежать. ;)
Алан Мур

Я иногда убегаю без необходимости, когда я не уверен, что-то особенное или нет. Я отредактировал ответ; это работает так же, но более кратко.
Лонни Бест

Глядя на это сейчас, я не знаю, почему я думал, что вы имели в виду \/, так как это сделало бы полную противоположность требованиям. Может быть, я думал, что вы предлагаете отрицательный шаблон фильтра.
Джастин Морган

54

Мне кажется, вы пытаетесь сопоставить теги без "/" в конце. Попробуй это:

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

8
Это не работает. Для ввода '<xa = "<b>" /> <y>' совпадают x и y, хотя x завершен.
ceving

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.