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

1324

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

<p>
<a href="foo">

Но не эти:

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

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

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

Я считаю, что он говорит:

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

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

Теги:
xhtml

35 ответов

4643
Лучший ответ

Вы не можете разобрать HTML-код [X] с регулярным выражением. Поскольку HTML не может быть проанализирован с помощью регулярных выражений. Regex не является инструментом, который можно использовать для правильного анализа HTML. Поскольку я уже много раз отвечал в вопросах HTML-и-regex, использование регулярных выражений не позволит вам потреблять HTML. Регулярные выражения - это инструмент, который недостаточно совершенен для понимания конструкций, используемых HTML. HTML не является регулярным языком и, следовательно, не может быть проанализирован регулярными выражениями. Запросы Regex не имеют возможности разбивать HTML на его значимые части. так много раз, но это не доходит до меня. Даже расширенные нерегулярные регулярные выражения, используемые Perl, не справляются с задачей анализа HTML. Вы никогда не заставите меня взломать. HTML - это язык достаточной сложности, который не может быть проанализирован с помощью регулярных выражений. Даже Джон Скит не может анализировать HTML, используя регулярные выражения. Каждый раз, когда вы пытаетесь проанализировать HTML с регулярными выражениями, нечестивый ребенок плачет кровью девственниц, а русские хакеры выкладывают ваш webapp. Разбор HTML с регулярным выражением вызывает тайные души в царство живых. HTML и регулярное выражение идут вместе, как любовь, брак и ритуал детоубийства. "Центр" не может удерживать слишком поздно. Сила регулярных выражений и HTML вместе в одном и том же концептуальном пространстве уничтожит ваш разум как много водянистую замазку. Если вы анализируете HTML с регулярным выражением, вы даете им и их богохульные способы, которые обрекают нас всех на бесчеловечные труды для Того, чье имя не может быть выражено на Основном многоязычном плане, он приходит. HTML-plus-regexp сжигает n erves разумного, пока вы наблюдаете, ваша психика увядает в натиске ужаса. Rege ̿̔̉ HTML-анализаторы на основе x - это рак, который убивает StackOverflow слишком поздно, что слишком поздно мы не можем быть сохранены trangession of child гарантирует, что регулярное выражение будет потреблять всю живую ткань (за исключением HTML, который он не может, как ранее пророчествовал) дорогой лорд поможет нам, как кто-либо сможет выжить в этом бедствии, используя регулярное выражение для анализа HTML, обрек человечество на вечность страшных пыток и дыр в безопасности , используя rege" > i > x как инструмент для обработки HTML устанавливает brea ch между этим миром и область страха c ͒ͪ o ͛ͫ прерывает сущности (например, SGML-сущности, но больше коррумпирован) простой glimp se of the reg ex parsers для HTML будет вставлять tantly transport ap rogrammer сознания i nto aw orl d непрекращающегося крика, он приходит , pestilent sl ithy regex-infection wil l пожирает ваш парсер HT, HT > / на все время, например Visual Basic хуже он приходит, com es не fi Ght h e com̡e̶s, ̕h̵i s un̨ho͞ly radiańcé de строит все enli ̍̈́̂̈́ ghtenment, теги HTML lea͠ki̧n͘g fr̶ǫm ̡yo ͟ur eye͢s̸ ̛l̕ik͏e liq uid p ain, песня reggular exp re ssion parsing будет exti nguish голоса mor tal man из sp здесь я могу видеть, что вы видите ̲͚̖͔̙ я ̩̂́ t ̲͎̩̱͔́̋̀ красиво t he f inal snuf fing o f the lie s Man ALL IS LOS ͖̩͇̗̪́̏̈́ TA LL я SL OST th e pon̷y он приходит s c ̶̮ om es co co me st he ich или permeat es al l MY FAC E MY FACE ᵒh god n o NO NOO̼ O ON Θ остановить t he a * ̶͑̾̾ ̅ͫ͏̙̤ g ͇̫͛͆̾ͫ̑͆ l ͖͉̗̩̳̟̍ͫͥͨ e ̠̅ s ͎a̧͈͖r̽̾̈́͒͑e n ot re ̀̑ͧ̌ aͨl ̘̝̙̃ͤ͂̾̆ ZA ̡͊͠͝ LGΌ IS ͮ̂҉̯͈͕̹̘̱ T O ͇̹̺ͅ Ɲ̴ȳ̳ TH̘ E ͖̈́̉ ͠P ̯͍̭ O̚ N̐Y̡ H ̸̡̪̯ͨ͊̽̅̾̎ E ̧̬̩̾͛ͪ̈́̀́͘ ̶̧̨̱̹̭̯ͧ̾ͬ C ̷̙̲̝͖ͭ̏ͥͮ͟ O ͮ͏̮̪̝͍ M ̲̖͊̒ͪͩͬ̚̚͜ E ̴̟̟͙̞̑ͩ͌͝ S ̨̥̫͎̭ͯ̿̔̀ͅ


Вместо этого вы пытались использовать синтаксический анализатор XML?


Замечание модератора

Это сообщение заблокировано, чтобы предотвратить его неправильное редактирование. Сообщение выглядит так, как должно выглядеть - проблем с его содержанием нет. Пожалуйста, не отмечайте это для нашего внимания.

  • 177
    Коби: Я думаю, что пришло время уйти с должности помощника «Не анализировать HTML с сотрудником Regex». Независимо от того, сколько раз мы говорим это, они не перестанут приходить каждый день ... даже каждый час. Это безнадежное дело, за которое кто-то еще может побороться. Так что продолжайте, разбирайте HTML с регулярным выражением, если нужно. Это всего лишь неработающий код, а не жизнь и смерть.
  • 27
    Можно ли использовать RegEx для анализа этого ответа?
Показать ещё 1 комментарий
3122

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

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

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

  • 117
    Кроме того, при разумном использовании scan & regex сбор достаточно регулярно отформатированных данных из больших документов будет намного быстрее, чем при использовании любого универсального синтаксического анализатора. И если вы знакомы с регулярными выражениями при кодировании, код будет быстрее, чем при кодировании xpath. И почти наверняка менее хрупкий к изменениям в том, что вы чистите. Так бле
  • 231
    @MichaelJohnston "Менее хрупкий"? Почти наверняка нет. Регулярные выражения заботятся о деталях форматирования текста, которые анализатор XML может игнорировать. Переключение между &foo; кодировки и разделы CDATA ? Используете HTML Minifier для удаления всех пробелов в документе, которые не отображаются в браузере? Синтаксический анализатор XML не будет заботиться, равно как и хорошо написанный оператор XPath. Основанный на регулярных выражениях "парсер", с другой стороны ...
Показать ещё 32 комментария
1741

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

  • 213
    ОП просит проанализировать очень ограниченное подмножество XHTML: начальные теги. То, что делает (X) HTML CFG, это его потенциальная возможность иметь элементы между начальным и конечным тегами других элементов (как в грамматическом правиле A -> s A e ). (X) HTML не имеет этого свойства в стартовом теге: стартовый тег не может содержать другие стартовые теги. Подмножество, которое OP пытается проанализировать, не является CFG.
  • 94
    В теории CS регулярные языки являются строгим подмножеством языков без контекста, но реализации регулярных выражений в основных языках программирования являются более мощными. Как описывает noulakaz.net/weblog/2007/03/18/… , так называемые «регулярные выражения» могут проверять простые числа в унарном, что, безусловно, не может быть выполнено регулярным выражением из теории CS.
Показать ещё 14 комментариев
1255

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

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

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

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

  • 72
    Деление на ноль - гораздо более простая проблема, чем другие, о которых вы упоминаете. Если вы используете интервалы, а не простую арифметику с плавающей запятой (которая должна быть у всех, но никто не такой), вы можете с радостью разделить что-то на [интервал, содержащий] ноль. Результатом является просто интервал, содержащий плюс и минус бесконечность.
  • 1
    @rjmunro Я чувствую, что использование интервалов для арифметики - это что-то потрясающее, но я никогда раньше этого не видел. Вы имеете в виду en.wikipedia.org/wiki/Interval_arithmetic ?
Показать ещё 13 комментариев
1080

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

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

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

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

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

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

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

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

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

  • 88
    Я хотел бы пойти с чем-то, что работает на вменяемых вещах, чем плакать о том, что я не идеален во всем :-)
  • 50
    Кто-то использует CDATA внутри HTML?
Показать ещё 6 комментариев
462

Есть люди, которые скажут вам, что Земля кругла (или, возможно, Земля - ​​сплющенный сфероид, если они хотят использовать странные слова). Они лгут.

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

Вы можете жить в своей реальности или принимать красную таблетку.

Как Лорд Маршал (является ли он относительным классом Маршала .NET?), я видел Underverse Stack Based Regex-Verse и возвращался с помощью powers знаний, которые вы не могу себе представить. Да, я думаю, что один из них защищал их, но они смотрели футбол по телевизору, поэтому это было не сложно.

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

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

Параметры для установки: RegexOptions.ExplicitCapture. Группа захвата, которую вы ищете, ELEMENTNAME. Если группа захвата ERROR не пуста, тогда была ошибка синтаксического анализа и регекс остановлен.

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

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 tokenize xml или полное регулярное выражение

  • 7
    @bemace Вы можете попробовать это ... В виртуальной машине ... отключен от Интернета и от вашей локальной сети ... Используя 10-футовый столб для запуска! :-)
  • 60
    Господи, это массивно. Мой самый большой вопрос - почему? Вы понимаете, что все современные языки имеют парсеры XML, верно? Вы можете сделать все это в 3 строки и быть уверенным, что это сработает. Кроме того, понимаете ли вы, что чистое регулярное выражение доказуемо неспособно делать определенные вещи? Если вы не создали гибридный синтаксический анализатор регулярных выражений и императивного кода, но он не выглядит так, как вы. Вы можете также сжать случайные данные?
Показать ещё 29 комментариев
300

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

  • sed, хотя:

    • Turing.sed
    • Создать HTML-парсер (домашнее задание)
    • ???
    • Profit!
  • hxselect из html-xml-utils package

  • vim/ex (который может легко перепрыгнуть между html-тегами), например:

    • удалить тег стиля с помощью внутреннего кода:

      $ curl -s http://example.com/ | ex -s +'/<style.*/norm nvatd' +%p -cq! /dev/stdin
      
  • grep, например:

    • извлечение внешнего html файла H1:

      $ curl -s http://example.com/ | grep -o '<h1>.*</h1>'
      <h1>Example Domain</h1>
      
    • извлечение тела:

      $ curl -s http://example.com/ | tr '\n' ' ' | grep -o '<body>.*</body>'
      <body> <div> <h1>Example Domain</h1> ...
      
  • html2text для простого синтаксического анализа текста:

  • с помощью xpath (XML::XPath perl module), см. здесь

  • perl или Python (см. пример @Gilles)

  • для разбора нескольких файлов одновременно, см. Как разобрать сто файлов html файлов в оболочке?


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

  • 12
    Смотрите также perlmonks.org/?displaytype=print;node_id=809842
  • 3
    Боюсь, вы не поняли шутку, @kenorb. Пожалуйста, прочитайте вопрос и принятый ответ еще раз. Речь идет не об инструментах синтаксического анализа HTML в целом, ни об инструментах оболочки синтаксического анализа HTML, а о синтаксическом анализе HTML с помощью регулярных выражений.
Показать ещё 6 комментариев
266

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

Microsoft фактически имеет раздел Рекомендации по регулярным выражениям в .NET Framework и, в частности, говорит о Рассмотрим [вход] источника входного сигнала.

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

Структура .NET уникальна, когда речь заходит о регулярных выражениях в том, что она поддерживает Балансирующие определения групп.

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

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

.NET Regular Expression Engine

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

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

Эти конструкторы позволяют регулярному выражению .NET эмулировать ограниченный КПК, по существу позволяющий простые версии стека операции: push, pop и empty. Простые операции в значительной степени эквивалентно приращению, декременту и сравнению с нулем соответственно. Это позволяет механизму регулярных выражений .NET распознавать подмножество контекстно-свободных языков, в частности те, которые только требуется простой счетчик. Это, в свою очередь, позволяет использовать нетрадиционные Регулярные выражения .NET для распознавания индивидуально сбалансированных конструкции.

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

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

Используйте флаги:

  • SingleLine
  • IgnorePatternWhitespace (необязательно, если вы сбрасываете регулярное выражение и удаляете все пробелы)
  • IgnoreCase (необязательно)

Объяснение регулярного выражения (inline)

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

Наконец, мне очень понравилась статья Джеффа Этвуда: Разбор Html Путь Ктулху. Забавно, он цитирует ответ на этот вопрос, который в настоящее время имеет более 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 = ...;)
Показать ещё 3 комментария
255

Я предлагаю использовать QueryPath для анализа XML и HTML в PHP. Это в основном тот же синтаксис, что и jQuery, только на стороне сервера.

  • 8
    @ Kyle - jQuery не анализирует XML, он использует встроенный синтаксический анализатор клиента (если он есть). Поэтому для этого вам не нужен jQuery, а всего лишь две строки простого старого JavaScript . Если встроенного парсера нет, jQuery не поможет.
  • 1
    @RobG На самом деле jQuery использует DOM, а не встроенный парсер.
Показать ещё 2 комментария
223

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

Рекомендуемое регулярное выражение неверно:

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

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

Мое предложение было бы

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

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

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

  • 24
    +1 за то, что вы заметили, что вопрос не в разборе полного (X) HTML, а в сопоставлении (X) открытых тегов HTML.
  • 9
    Что-то еще, что большинство ответов, кажется, игнорирует, - то, что анализатор HTML может очень хорошо использовать регулярные выражения в его реализации для частей HTML, и я был бы удивлен, если бы большинство анализаторов не делали этого.
Показать ещё 3 комментария
178

Try:

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

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

  • 106
    <a href="foo" title="5> 3 "> К сожалению </a>
  • 21
    Это очень верно, и я подумал об этом, но я предположил, что символ > правильно экранирован на & gt ;.
Показать ещё 6 комментариев
167

Sun Tzu, древний китайский стратег, генерал и философ, сказал:

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

В этом случае ваш враг - это HTML, и вы либо сами, либо регулярное выражение. Возможно, вы даже Perl с нерегулярным регулярным выражением. Знайте HTML. Знай себя.

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

HTML has
complexity exceeding
regular language.

Я также написал хайку, описывающий характер регулярных выражений в Perl.

The regex you seek
is defined within the phrase
<([a-zA-Z]+)(?:[^>]*[^/]*)?>
161
<?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"

В основном просто определяйте имена элементов node, которые закрываются самостоятельно, загружают всю строку html в библиотеку DOM, захватывают все элементы, перебирают и отфильтровывают те, которые не закрываются и не работают на них.

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

  • 1
    Если вы имеете дело с настоящим XHTML, тогда добавьте getElementsByTagName с NS и укажите пространство имен.
156

Я не знаю вашей конкретной потребности в этом, но если вы также используете .NET, не могли бы вы использовать Html Agility Pack

Выдержки:

Это библиотека кода .NET, которая позволяет вы разбираете "вне Интернета" HTML файлы. Парсер очень толерантен с искаженным HTML-кодом "реального мира".

139

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

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

<foo><bar/></foo>

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

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

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

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

105

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

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

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

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

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

Для Python и Java были опубликованы похожие ссылки.

Для downvoters - я написал свой класс только тогда, когда синтаксические анализаторы XML оказались не в состоянии противостоять реальному использованию. Религиозное downvoting просто препятствует тому, чтобы полезные ответы были отправлены - держите вещи в пределах перспективы вопроса, пожалуйста.

94

Всякий раз, когда мне нужно быстро извлечь что-то из HTML-документа, я использую Tidy, чтобы преобразовать его в XML, а затем использовать XPath или XSLT, чтобы получить то, что мне нужно. В вашем случае что-то вроде этого:

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

Мне нравится анализировать HTML с регулярными выражениями. Я не пытаюсь разбирать идиот HTML, который намеренно нарушен. Этот код является моим основным синтаксическим анализатором (Perl edition):

$_ = 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 и т.д. Я даже не шучу:) Наслаждайтесь.

Достаточно просто перегрузить мой slurp-all-first Perl script в приятную поточную передачу, если вы хотите обрабатывать огромные веб-страницы. Но это действительно не нужно.

Бьюсь об заклад, я заберусь для этого.

Разделение HTML


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

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

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

С небольшими вариациями он может справиться с беспорядочным HTML... или сначала конвертировать HTML → XHTML.


Лучший способ писать регулярные выражения - это Lex/Yacc, а не как непрозрачные однострочные или прокомментированные многострочные чудовища. Я не делал этого здесь; эти им едва ли нужны.

  • 34
    «Я не пытаюсь разобрать идиота HTML, который намеренно нарушен». Как ваш код узнает разницу?
  • 0
    Ну, это не имеет большого значения, если HTML сломан или нет. Эта вещь все еще будет разбивать HTML на теги и текст. Единственное, что могло бы испортить это, если бы люди включали неэкранированные символы <или> в текст или атрибуты. На практике мой крошечный HTML-сплиттер работает хорошо. Мне не нужен огромный чудовищный бит, полный эвристики. Простые решения не для всех ...!
Показать ещё 7 комментариев
86

Здесь решение:

<?php
// here 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 get the occurrences:
preg_match_all($pattern, $string, $matches, PREG_PATTERN_ORDER);

// print the result:
print_r($matches[0]);
?>

Чтобы проверить это глубоко, я ввел в теги автозапуска строки, например:

  • < hr/ >
  • < ш / >
  • < бр >

Я также ввел теги с помощью:

  • один атрибут
  • более одного атрибута
  • значение которого привязано либо в одинарные кавычки, либо в двойные кавычки
  • содержащие одинарные кавычки, когда разделителем является двойная кавычка и наоборот
  • "unpretty" с пробелом перед символом "=", после него и до и после него.

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

< РЕДАКТИРОВАТЬ > Я забыл, что вопрос от пользователя заключался в том, чтобы избежать разбора самозакрывающихся тегов. В этом случае шаблон проще, превращаясь в это:

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

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

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

</РЕДАКТИРОВАТЬ >

Понимание шаблона

Если кто-то заинтересован узнать больше о шаблоне, я предоставляю некоторую строку:

  • первое подвыражение (\ w +) соответствует имени тега
  • второе подвыражение содержит шаблон атрибута. Он состоит из:
    • одно или несколько пробелов \s +
    • имя атрибута (\ w +)
    • ноль или более пробелов \s * (возможно или нет, оставляя пробелы здесь)
    • символ "="
    • снова, ноль или более пробелов
    • разделитель значения атрибута, одинарная или двойная кавычка ( "|" ). В шаблоне одиночная кавычка экранируется, потому что она совпадает с разделителем строки PHP. Это подвыражение захватывается круглыми скобками, поэтому можно снова ссылаться, чтобы разобрать закрытие атрибута, поэтому очень важно.
    • значение атрибута, соответствующее почти любому: (. *?); в этом конкретном синтаксисе, используя жадное соответствие (знак вопроса после звездочки), механизм RegExp включает в себя оператор "look-ahead", который соответствует чему угодно, но что следует за этим подвыражением
    • вот весело: the\4 part - это оператор backreference, который ссылается на подвыражение, определенное ранее в шаблоне, в данном случае я имею в виду четвертое подвыражение, который является первым разделителем атрибутов, найденным
    • ноль или более пробелов \s *
    • конец суб-выражения атрибута заканчивается здесь, с указанием нуля или более возможных вхождений, заданных звездочкой.
  • Затем, поскольку тег может заканчиваться пробелом перед " > ", символ, ноль или более пробелов сопоставляются с подшаблоном \s *.
  • Соответствующий тег может заканчиваться простым " > " символ или возможное закрытие XHTML, которое использует перед ним косую черту: (/" > | > ). Слэш, конечно, сбежал, поскольку он совпадает с разделителем регулярных выражений.

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

  • 12
    Не соответствует допустимым тегам, имеющим атрибуты без значения, т.е. <option selected> . Также не сопоставляет допустимые теги со значениями атрибутов без кавычек, т.е. <p id=10> .
  • 1
    @ridgerunner: Большое спасибо за ваш комментарий. В этом случае шаблон должен немного измениться: $ pattern = '/ <(\ w +) (\ s + (\ w +) (\ s * \ = \ s * (\' | "|) (. *?) \\ 5 \ s *)?) * \ S *> / '; я проверил это и работает в случае атрибутов без кавычек или атрибутов без значения.
Показать ещё 4 комментария
86

Я использовал инструмент с открытым исходным кодом под названием HTMLParser. Он предназначен для разбора HTML по-разному и служит цели достаточно хорошо. Он может анализировать HTML как отличный treenode, и вы можете легко использовать его API для получения атрибутов из node. Проверьте это и посмотрите, поможет ли это вам.

72

Есть несколько хороших регулярных выражений для замены HTML на BBode здесь. Обратите внимание на то, что он не пытается полностью разобрать 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
    Не делай этого. Пожалуйста.
70

Вот парсер PHP на основе PHP, который анализирует HTML с использованием некоторого нечестивого регулярного выражения. Как автор этого проекта, я могу сказать вам, что можно анализировать HTML с регулярным выражением, но не эффективно. Если вам нужно решение на стороне сервера (как я сделал для моего wp-Typography WordPress plugin), это работает.

  • 1
    htmlawed - это еще один PHP-проект, который анализирует HTML для фильтрации, преобразования и т. д. Имеет хороший код, если вы можете это понять!
  • 0
    Нет, вы не можете разобрать HTML с регулярным выражением. Но для некоторых подмножеств это может работать.
66

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

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

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

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

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

Здесь волшебный паттерн:

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

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

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

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

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

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

  • 35
    Я помещу это в «Regex, который не допускает больше, чем в атрибутах». Сверьтесь с <input value = "is 5> 3?" />
  • 68
    Если вы добавите что-то подобное в производственный код, вас, скорее всего, застрелит сопровождающий. Жюри никогда не осудит его.
Показать ещё 10 комментариев
64

Как уже отмечалось многими, 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$

Backreferencing FTW!

  • 0
    @ GlitchMr, это была его точка зрения. Современные регулярные выражения не являются технически регулярными, и для этого нет никаких оснований.
  • 3
    @alanaktion: «современные» регулярные выражения (читай: с расширениями Perl) не могут совпадать в пределах O(MN) (M - длина регулярного выражения, N - длина текста). Обратные ссылки являются одной из причин этого. Реализация в awk не имеет обратных ссылок и соответствует всему за время O(MN) .
57

Недавно я написал HTML-дезинфицирующее средство в Java. Он основан на смешанном подходе регулярных выражений и Java-коде. Лично я ненавижу регулярные выражения и его глупость (читаемость, ремонтопригодность и т.д.), Но если вы уменьшите объем своих приложений, это может соответствовать вашим потребностям. Во всяком случае, мой sanitizer использует белый список для HTML-тегов и черный список для некоторых атрибутов стиля.

Для вашего удобства я создал игровую площадку, чтобы вы могли проверить, соответствует ли код вашим требованиям: игровая площадка и код Java. Ваш отзыв будет оценен.

Существует небольшая статья, описывающая эту работу в моем блоге: http://roberto.open-lab.com

54

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

<([a-zA-Z][a-zA-Z0-9]*)[^>]*(?<!/)>
  • 8
    Это не работает. Для ввода '<xa = "<b>" /> <y>' совпадают x и y, хотя x завершен.
52

Если вы просто пытаетесь найти эти теги (без амбиций синтаксического анализа), попробуйте это регулярное выражение:

/<[^/]*?>/g

Я написал его через 30 секунд и протестировал здесь: http://gskinner.com/RegExr/

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

  • 2
    Я думаю, что вы имеете в виду \/> вместо \\> .
  • 0
    Нет, просто \> это то, что я имел в виду; Я никогда не хотел редактировать регулярное выражение моего исходного поста.
Показать ещё 3 комментария
50

Верно, что при программировании обычно лучше использовать выделенные парсеры и API вместо обычных выражений при работе с HTML, особенно если точность имеет первостепенное значение (например, если ваша обработка может иметь последствия для безопасности). Однако я не отношусь к догматическому мнению о том, что разметка в стиле XML никогда не должна обрабатываться с помощью регулярных выражений. Бывают случаи, когда регулярные выражения являются отличным инструментом для работы, например, когда вы делаете одноразовые изменения в текстовом редакторе, исправляете поврежденные файлы XML или обрабатываете форматы файлов, которые выглядят как arent, а не XML. Есть некоторые проблемы, о которых нужно знать, но они не являются непреодолимыми или даже обязательно релевантными.

Простое регулярное выражение, подобное <([^>"']|"[^"]*"|'[^']*')*>, обычно достаточно хорошо, в тех случаях, о которых я только что упомянул. Это наивное решение, все рассмотренное, но оно корректно допускает символы unencoded > в значениях атрибутов. Если вы ищете, например, тег 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*)>

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

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

48

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

Существует окончательное сообщение в блоге о совпадении сокровенных HTML-элементов, написанных Стивеном Левитаном.

43

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

<([a-zA-Z]+)(?:[^>]*[^/] *)?> 

должен делать то, что вам нужно. Но я думаю, что решение "мориц" уже прекрасное. Я не видел этого в начале.

Для всех downvoters: в ​​некоторых случаях имеет смысл использовать регулярное выражение, потому что это может быть самым простым и быстрым решением. Я согласен с тем, что в общем случае вы не должны анализировать HTML с регулярным выражением. Но регулярное выражение может быть очень мощным инструментом, когда у вас есть подмножество HTML, где вы знаете формат, и вы просто хотите извлечь некоторые значения. Я делал это сотни раз и почти всегда добивался того, чего хотел.

40

OP, похоже, не говорит, что ему нужно делать с тегами. Например, нужно ли ему извлекать внутренний текст или просто проверять теги?

Я твердо в лагере, который говорит, что RegEx - это не полный, полный текстовый синтаксический анализатор. Я написал большое количество текстового синтаксического кода, включая этот код для анализа HTML-тегов.

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

33

Это может сделать:

<.*?[^/]>

Или без конечных тегов:

<[^/].*?[^/]>

Что с пламенными войнами на парсерах HTML? Парсеры HTML должны анализировать (и перестраивать!) Весь документ, прежде чем он сможет классифицировать ваш поиск. Регулярные выражения могут быть более быстрыми/изящными при определенных обстоятельствах. Мои 2 цента...

19

Я думаю, что это может работать

<[a-z][^<>]*(?:(?:[^/]\s*)|(?:\s*[^/]))>

И это может быть протестировано здесь.


По w3schools...

Правила именования XML

Элементы XML должны следовать этим правилам именования:

  • Имена могут содержать буквы, цифры и другие символы
  • Имена не могут начинаться с цифры или символа пунктуации
  • Имена не могут начинаться с букв xml (или XML, или Xml и т.д.)
  • Имена не могут содержать пробелы
  • Любое имя может быть использовано, слова не зарезервированы.

И шаблон, который я использовал, будет придерживаться этих правил.

  • 64
    Предупреждение: w3schools не следует рассматривать как авторитетную или надежную ссылку ( ссылка ). В любом случае, перечисленные вами правила применяются только к именам элементов и атрибутов; Значения атрибута гораздо более гибкие. Вы можете избежать запрета > (который является законным, но редко используется), но представьте атрибут HREF без косых черт! ;)
  • 4
    Это выражение будет работать для многих имен элементов, однако в спецификации XML используется буква в смысле Unicode. Существуют допустимые имена элементов, которым это не будет соответствовать.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню