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

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

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

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

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

Ответы

Ответ 1

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

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

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

Ответ 2

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

//a[@class and @title]

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

Ответ 3

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

<a\b[^<>]*>

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

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

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

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

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

Ответ 4

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

Что-то вроде этого (untested, используя синтаксис regex.net с символами \w для слов и \s для пробелов):

<a ((?<key>\w+)\s?=\s?['"](?<value>\w+)['"])+ />

Ответ 5

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

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

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

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

Ответ 6

Первым специальным решением может быть следующее:

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

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

Ответ 7

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

Предположим, что вы хотите соответствовать любой из этих шести строк:

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