Как я могу получить набор значений из вложенных HTML-подобных элементов с помощью RegExp?

У меня проблема с созданием регулярного выражения для следующей задачи:

Предположим, у нас есть HTML-подобный текст вида:

<x>...<y>a</y>...<y>b</y>...</x>

Я хочу получить набор значений внутри тегов <y></y>, расположенных внутри заданного тега <x>, поэтому результатом приведенного выше примера будет набор из двух элементов ["a", "b"].

Кроме того, мы знаем, что:

  • Теги <y> не могут быть заключены в другие теги <y>
  • ... может включать любой текст или другие теги.

Как я могу добиться этого с помощью RegExp?


person Community    schedule 04.12.2008    source источник


Ответы (4)


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

person Bill the Lizard    schedule 04.12.2008
comment
к сожалению, не гарантируется, что текст будет действительным XML или в формате HTML. - person ; 04.12.2008
comment
Если предположить, что это HTML, стандартный ответ — запустить его через Tidy. С параметром --clean Tidy выводит только допустимый XHTML, который должен анализироваться практически любым пакетом HTML/XML. - person Eli; 04.12.2008
comment
+1 это - почему в наши дни все хотят анализировать XML с помощью регулярных выражений? - person annakata; 04.12.2008
comment
Во-первых, как я уже сказал — это не XML. Во-вторых, я не хочу участвовать в обработке этого текста с помощью каких-либо инструментов, чтобы преобразовать его в хороший HTML/XML только для того, чтобы иметь возможность анализировать его с помощью стандартных библиотек HTML/XML. Это накладные расходы. Текст, о котором я говорю, маленький и простой. RegEx — это именно то, что здесь соответствует. - person ; 04.12.2008
comment
Обработка и синтаксический анализ будут намного проще, чем регулярное выражение, когда у вас есть неограниченное количество потенциальных совпадений. Попробуйте инструменты, которые были разработаны для решения этой проблемы, прежде чем создавать себе еще больше проблем. - person Bill the Lizard; 04.12.2008
comment
+1 за то, что не поддались распространенному неправильному использованию регулярных выражений при разборе вложенных структур. - person Marko Dumic; 05.12.2008
comment
Я могу порекомендовать Beautiful Soup для Python. Идеально подходит для анализа недопустимых данных HTML/XML. - person Kim; 16.05.2009

Я верю вам на слово:

"y" tags cannot be enclosed in other "y" tags

input looks like: <x>...<y>a</y>...<y>b</y>...</x>

и то, что все остальное тоже не вложено и правильно отформатировано. (Отказ от ответственности: если это не так, это не моя вина.)

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

<x[^>]*>(.*?)</x>

Затем (в теле цикла) найдите любые теги Y в группе соответствия 1 «внешнего» совпадения сверху:

<y[^>]*>(.*?)</y>

Псевдокод:

input = "<x>...<y>a</y>...<y>b</y>...</x>"
x_re  = "<x[^>]*>(.*?)</x>"
y_re  = "<y[^>]*>(.*?)</y>"

for each x_match in input.match_all(x_re)
  for each y_match in x_match.group(1).value.match_all(y_re)
    print y_match.group(1).value
  next y_match
next x_match

Псевдовывод:

a
b

Дальнейшие разъяснения в комментариях показали, что в любом элементе X есть произвольное количество элементов Y. Это означает, что не может быть ни одного регулярного выражения, которое соответствует им и извлекает их содержимое.

person Tomalak    schedule 04.12.2008
comment
Спасибо. Не упоминал об этом с самого начала, но я понимаю, как достичь цели с помощью циклов. Но я на 100% уверен, что есть решение, использующее операцию одиночного совпадения, и это то, что я пытаюсь выяснить :) - person ; 04.12.2008
comment
Если нет жесткого ограничения на количество элементов Y, то нет решения только для регулярных выражений. Почему ты так уверен, что есть? Возможно, в вопросе не хватает деталей. - person Tomalak; 04.12.2008
comment
Как я уже сказал, в вашем вопросе отсутствуют некоторые подробности о точной структуре строк, с которыми вы ожидаете иметь дело. - person Tomalak; 04.12.2008
comment
нет ограничений на количество тегов y (следовательно, нет ограничений на размер результирующей коллекции). какие еще детали, по вашему мнению, мне нужно предоставить? - person ; 04.12.2008
comment
вложенность или отсутствие вложенности, элементы могут иметь атрибуты или нет, они сгенерированы машиной или введены пользователем, и последнее, но не менее важное: что вы на самом деле пытаетесь сделать? Может быть, есть лучшие способы, чем регулярное выражение, чтобы добраться туда. - person Tomalak; 04.12.2008
comment
y не может быть вложен в другой y, но может быть вложен в какой-либо другой тег, скажем, z; сгенерированный машиной недействительный HTML (подобный HTML), который я не могу контролировать; скажем так - в настоящее время я хотел бы знать, возможно ли выполнить задачу с одним соответствием регулярному выражению - person ; 04.12.2008
comment
и атрибуты возможны внутри любого тега (включая x и y) - person ; 04.12.2008
comment
Хорошо, я вижу. Если вы хотите получить текстовое содержимое тегов Y, вы не сможете сделать это с помощью одного регулярного выражения, точка. Это не то, как работают регулярные выражения. Используйте цикл, как указано, я расширю свое регулярное выражение, чтобы учесть атрибуты. - person Tomalak; 04.12.2008
comment
с совпадениями подгруппы подшаблона это должно работать, но это зависит от языка, потому что не все языки поддерживают эту функцию регулярных выражений. - person hakre; 19.08.2011

Коротко и просто: используйте XPath :)

person Guðmundur Bjarni    schedule 04.12.2008
comment
В комментарии к @Bill the Lizard он сказал, что не гарантируется, что текст будет действительным XML. - person Tomalak; 04.12.2008

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

String str = "<y>c</y>...<x>...<y>a</y>...<y>b</y>...</x>...<y>d</y>";
String regex = "<y[^>]*+>(?=(?:[^<]++|<(?!/?+x\\b))*+</x>)(.*?)</y>";
Matcher m = Pattern.compile(regex).matcher(str);
while (m.find())
{
  System.out.println(m.group(1));
}

Как только я сопоставил <y>, я использую просмотр вперед, чтобы подтвердить, что где-то впереди есть </x>, но между текущей позицией и ней нет <x>. Предполагая, что псевдо-HTML имеет достаточно правильный формат, это означает, что текущая позиция совпадения находится внутри элемента «x».

Я активно использовал притяжательные квантификаторы, потому что они значительно упрощают такие вещи, но, как вы можете видеть, регулярное выражение все еще немного чудовищно. Помимо Java, единственными известными мне разновидностями регулярных выражений, которые поддерживают притяжательные квантификаторы, являются инструменты PHP и JGS (RegexBuddy/PowerGrep/EditPad Pro). С другой стороны, многие языки предоставляют способ получить все совпадения одновременно, но в Java мне пришлось писать для этого собственный цикл.

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

person Alan Moore    schedule 05.12.2008