Синтаксис регулярного выражения с переменным порядком
Есть ли способ указать, что две или более фразы регулярных выражений могут возникать в любом порядке? Например, атрибуты 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>/