Синтаксис регулярных выражений с переменным порядком - PullRequest
6 голосов
/ 31 марта 2009

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

<a href="home.php" class="link" title="Home">Home</a>
<a href="home.php" title="Home" class="link">Home</a>

Как мне написать совпадение, которое проверяет класс и заголовок и работает в обоих случаях? Я в основном ищу синтаксис, который позволяет мне проверять в любом порядке, а не просто сопоставлять класс и заголовок, как я могу это сделать. Есть ли способ, кроме как просто включить обе комбинации и соединить их знаком '|'?

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

Ответы [ 7 ]

8 голосов
/ 31 марта 2009

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

С другой стороны, я бы не стал делать это с RE, поскольку они не предназначены для языков программирования. Что плохого в старомодном подходе использования библиотеки обработки XML?

Если вам требуется для использования RE, этот ответ, вероятно, не сильно поможет, но я верю в использование правильных инструментов для работы.

5 голосов
/ 31 марта 2009

Рассматривали ли вы xpath? (где порядок атрибутов не имеет значения)

//a[@class and @title]

Выберет оба <a> узла в качестве допустимых совпадений. Единственное предостережение в том, что ввод должен быть xhtml (правильно сформированный xml).

4 голосов
/ 31 марта 2009

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

<a\b[^<>]*>

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

(?=[^<>]*\s+class="link")
(?=[^<>]*\s+title="Home")

[^<>]* позволяет сканировать атрибут заранее, но не позволяет ему выйти за пределы закрывающей угловой скобки. Сопоставление начальных пробелов здесь, в запросе, служит двум целям: оно более гибкое, чем сопоставление с базовым регулярным выражением, и гарантирует, что мы сопоставляем полное имя атрибута. Объединяя их, мы получаем:

<a\b(?=[^<>]*\s+class="link")(?=[^<>]*\s+title="Home")[^<>]+>[^<>]+</a>

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

2 голосов
/ 31 марта 2009

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

Примерно так (не тестировалось, с использованием синтаксиса регулярных выражений .net с \ w для символов слова и \ s для пробела):

<a ((?<key>\w+)\s?=\s?['"](?<value>\w+)['"])+ />
1 голос
/ 31 марта 2009

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

С одним регулярным выражением вам понадобится что-то вроде

<a[^>]*((class="([^"]*)")|(title="([^"]*)"))?((title="([^"]*)")|(class="([^"]*)"))?[^>]*>

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

0 голосов
/ 31 марта 2009

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

Допустим, вы хотите сопоставить любую из этих шести строк:

123-abc-456-def-789-ghi-0AB
123-abc-456-ghi-789-def-0AB
123-def-456-abc-789-ghi-0AB
123-def-456-ghi-789-abc-0AB
123-ghi-456-abc-789-def-0AB
123-ghi-456-def-789-abc-0AB

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

/123-(abc|def|ghi)-456-(?!\1)(abc|def|ghi)-789-(?!\1|\2)(abc|def|ghi)-0AB/

Обратные ссылки (\1, \2), позволяют ссылаться на ваши предыдущие совпадения, и ноль Соответствие ширины вперед ((?!...)) позволяет отменить позиционное соответствие, говоря, что не совпадает, если содержал спички на этой позиции. Сочетание двух гарантирует, что ваш матч является законной перестановкой из указанных элементов, с каждой возможностью только один раз.

Так, например, в ruby:

input = <<LINES
123-abc-456-abc-789-abc-0AB
123-abc-456-abc-789-def-0AB
123-abc-456-abc-789-ghi-0AB
123-abc-456-def-789-abc-0AB
123-abc-456-def-789-def-0AB
123-abc-456-def-789-ghi-0AB
123-abc-456-ghi-789-abc-0AB
123-abc-456-ghi-789-def-0AB
123-abc-456-ghi-789-ghi-0AB
123-def-456-abc-789-abc-0AB
123-def-456-abc-789-def-0AB
123-def-456-abc-789-ghi-0AB
123-def-456-def-789-abc-0AB
123-def-456-def-789-def-0AB
123-def-456-def-789-ghi-0AB
123-def-456-ghi-789-abc-0AB
123-def-456-ghi-789-def-0AB
123-def-456-ghi-789-ghi-0AB
123-ghi-456-abc-789-abc-0AB
123-ghi-456-abc-789-def-0AB
123-ghi-456-abc-789-ghi-0AB
123-ghi-456-def-789-abc-0AB
123-ghi-456-def-789-def-0AB
123-ghi-456-def-789-ghi-0AB
123-ghi-456-ghi-789-abc-0AB
123-ghi-456-ghi-789-def-0AB
123-ghi-456-ghi-789-ghi-0AB
LINES

# outputs only the permutations
puts input.grep(/123-(abc|def|ghi)-456-(?!\1)(abc|def|ghi)-789-(?!\1|\2)(abc|def|ghi)-0AB/)

Для перестановки из пяти элементов это будет:

/1-(abc|def|ghi|jkl|mno)-
 2-(?!\1)(abc|def|ghi|jkl|mno)-
 3-(?!\1|\2)(abc|def|ghi|jkl|mno)-
 4-(?!\1|\2|\3)(abc|def|ghi|jkl|mno)-
 5-(?!\1|\2|\3|\4)(abc|def|ghi|jkl|mno)-6/x

Для вашего примера, регулярное выражение будет

/<a href="home.php" (class="link"|title="Home") (?!\1)(class="link"|title="Home")>Home<\/a>/
0 голосов
/ 31 марта 2009

Первое специальное решение может заключаться в следующем:

((class|title)="[^"]*?" *)+

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

...