В чисто теоретическом смысле регулярные выражения не могут проанализировать XML.Они определены таким образом, что не позволяют им запоминать какое-либо предыдущее состояние, тем самым предотвращая правильное сопоставление произвольного тега, и они не могут проникнуть на произвольную глубину вложенности, поскольку вложенность должна быть встроена в регулярное выражение.
Современные парсеры регулярных выражений, однако, созданы для их удобства для разработчика, а не для их соблюдения точного определения.Таким образом, у нас есть такие вещи, как обратные ссылки и рекурсия, которые используют знания предыдущих состояний.Используя их, очень просто создать регулярное выражение, которое может исследовать, проверять или анализировать XML.
Рассмотрим, например,
(?:
<!\-\-[\S\s]*?\-\->
|
<([\w\-\.]+)[^>]*?
(?:
\/>
|
>
(?:
[^<]
|
(?R)
)*
<\/\1>
)
)
. Здесь будет найден следующий правильно сформированный тег XML иликомментарий, и он найдет его, только если все его содержимое правильно сформировано. (Это выражение было протестировано с использованием Notepad ++, который использует библиотеку регулярных выражений Boost C ++, которая очень похожа на PCRE.)
Вот как это работает:
- Первоечанк соответствует комментарию.Это необходимо сделать первым, чтобы иметь дело с любым закомментированным кодом, который в противном случае мог бы вызвать зависания.
- Если это не совпадает, он будет искать начало тега.Обратите внимание, что для захвата имени используются круглые скобки.
- Этот тег будет либо заканчиваться на
/>
, таким образом заканчивая тег, либо заканчиваться на >
, и в этом случае он будет продолжен путем изучениясодержимое тега. - Он продолжит синтаксический анализ, пока не достигнет
<
, после чего он вернется к началу выражения, позволяя ему иметь дело либо с комментарием, либо с новым тегом. - Он будет продолжаться в цикле, пока не достигнет конца текста или значения
<
, которое не может быть проанализировано.Несоответствие, конечно, заставит его начать процесс заново.В противном случае <
, вероятно, является началом закрывающего тега для этой итерации.Используя обратную ссылку внутри закрывающего тега <\/\1>
, он будет соответствовать открывающему тегу для текущей итерации (глубина).Есть только одна группа захвата, так что это совпадение очень просто.Это делает его независимым от имен используемых тегов, хотя вы можете изменить группу захвата для захвата только определенных тегов, если вам нужно. - В этот момент он либо выйдет из текущей рекурсии, вверхперейти к следующему уровню или завершиться совпадением.
В этом примере решаются проблемы, связанные с пробелами или идентификацией соответствующего содержимого, с помощью групп символов, которые просто сводят на нет <
или >
, или вв случае комментариев, используя [\S\s]
, который будет соответствовать чему угодно, включая возврат каретки и новые строки, даже в однострочном режиме, продолжая, пока не достигнет -->
.Следовательно, он просто обрабатывает все как допустимые, пока не достигнет чего-то значимого.
Для большинства целей такое выражение не особенно полезно.Он подтвердит, что XML сформирован правильно, но это все, что он действительно сделает, и не учитывает свойства (хотя это было бы простым дополнением).Это так просто, потому что не учитывает такие проблемы реального мира, как определения имен тегов.Приспособление этого к реальному использованию сделало бы это намного больше зверя.Вообще, настоящий парсер XML был бы намного лучше.Этот, вероятно, лучше всего подходит для обучения работе рекурсии.
Короче говоря: используйте парсер XML для реальной работы и используйте его, если вы хотите поиграть с регулярными выражениями.