В общем, наверное, как и другой любой начинающий JavaScript прогрммист (2 года назад), мне хотелось все реализовать своими руками. Так возникло ужасающее очень быстрое регулярное выражение из 280 символов.
Приблизительно полтора года назад, я узнал о библиотеке yass, которая была самым быстрым инструментом для поиска DOM элементов в JavaScript по CSS селекторам (ссылка на тесты).
И тут у меня возник ужасный интерес. Я захотел придумать способ, который будет еще быстрее. В то время я как раз читал книгу «Регулярные выражения Библиотека программиста» второе издание от Дж. Фридла. И вот… Это было лето, я еще был студентом и у меня была масса времени. Работа закипела…
Статью я решил написать именно из-за следующего выражения, которое умеет почти полностью проанализировать CSS селектор-запрос (даже немного продвинутый, выходящий за рамки стандарта CSS3):
Скажу сразу, что нормальный человек, в таком виде, не поймет в строках выше ничего! Я, относящийся к числуненормальных, чтобы это написать сделал анализатор регулярных выражений на JavaScript. По сути получилась простая форма: в одно поле — регулярное выражение, в другое — строку для поиска и третье — результат, несколько checkbox-ов.
Напишем это выражение в читабельном виде, используя модификатор «х» (я реализовалбажную его эмуляцию для JavaScript).
Сразу чтобы было понятнои я не писал для себя или гуру регулярных выражений скажу, что в этом выражении очень часть повторяется конструкция вида «начало (нормальные символы)*(спец символы (нормальные символы)*)* конец». Это почти универсальная конструкция нахождения чего-либо между какими-то символами, например, поиск текста между кавычками и допускаются вложенные кавычки с учетом экранирования. Более детальную информацию можно найти в выше упомянутой книге, в разделе «Построение эффективных регулярных выражений».
В нашем случае это касается поиска текста между кавычками (" и '), круглыми и квадратными скобками, а также символами "+", ">", "~", ",", " ", ":".
Основой построения этого выражения является возможность разбить CSS селектор на части. Я разбил его так:
Сопоставим теперь все это с выражением.
Первая часть ищет или "+", ">", ",", "~", "\s+", если не находит, тогда ищем все что между ними.
Вторая — обрабатывает квадратные скобки. Шаблон "[^=]+=~?\s*" был построен для того, чтобы можно было делать поиск по атрибут селекторам, с помощью сколь угодно сложных регулярных выражений.
Третья — находит совпадения для псевдо селектора, при чем круглые скобки ставить не обязательно.
Все символы можно экранировать с помощью обратного слэша ("\") или же взять выражение в одинарные или же двойные кавычки, тогда они не будут восприняты как управляющие.
Думаю дальше уже понятно насколько просто написать парсер CSS3 селекторов. Кому интересно поэкспериментировать — идите сюда. Буду очень благодарен, если кто-то выскажется на счет улучшения скорости роботы или строгости регулярного выражения.
Ну и конечно же, большое спасибо Дж. Фридлу, автору серии бесценных книг по регулярным выражениям
P.S.: извиняюсь за бажность анализатора регулярных выражений. Он был создан как промежуточный этап (работает в Chrome и FF точно). Если что-то не работает, там стоит callback на событие change, нажимайте на checkbox-и или просто вставте пробел в поле с регулярным выражением, должно запустится.
Немного истории
Приблизительно полтора года назад, я узнал о библиотеке yass, которая была самым быстрым инструментом для поиска DOM элементов в JavaScript по CSS селекторам (ссылка на тесты).
И тут у меня возник ужасный интерес. Я захотел придумать способ, который будет еще быстрее. В то время я как раз читал книгу «Регулярные выражения Библиотека программиста» второе издание от Дж. Фридла. И вот… Это было лето, я еще был студентом и у меня была масса времени. Работа закипела…
Чего шумим?
Статью я решил написать именно из-за следующего выражения, которое умеет почти полностью проанализировать CSS селектор-запрос (даже немного продвинутый, выходящий за рамки стандарта CSS3):
/(?:(?:\s*[+>~,]\s*|\s+)|[^:+>~,\s\\[\]]+(?:\\.[^:+>~,\s\\[\]]*)*)|\[(?:[^\\[\]]*(?:\\.[^\\[\]]*)*|[^=]+=~?\s*(?:"[^\\"]*(?:\\.[^"\\]*)*"|'[^\\']*(?:\\.[^'\\]*)*'))\]|:[^\\:([]+(?:\\.[^\\:([]*)*(?:\((?:[^\\()]*(?:\\.[^\\()]*)*|"[^\\"]*(?:\\.[^"\\]*)*"|'[^\\']*(?:\\.[^'\\]*)*')\))?/g
Давайте дружить
Скажу сразу, что нормальный человек, в таком виде, не поймет в строках выше ничего! Я, относящийся к числу
Напишем это выражение в читабельном виде, используя модификатор «х» (я реализовал
(?:
(?:\s*[+>~,]\s*|\s+)
|
[^:+>~,\s\\[\]]+(?:\\.[^:+>~,\s\\[\]]*)*
)
|
\[(?:
[^\\[\]]*(?:\\.[^\\[\]]*)*
|
[^=]+=~?\s*
(?:
"[^\\"]*(?:\\.[^"\\]*)*"
|
'[^\\']*(?:\\.[^'\\]*)*'
)
)\]
|
:[^\\:([]+(?:\\.[^\\:([]*)*
(?:
\((?:
[^\\()]*(?:\\.[^\\()]*)*
|
"[^\\"]*(?:\\.[^"\\]*)*"
|
'[^\\']*(?:\\.[^'\\]*)*'
)\)
)?
Немного теории
Сразу чтобы было понятно
В нашем случае это касается поиска текста между кавычками (" и '), круглыми и квадратными скобками, а также символами "+", ">", "~", ",", " ", ":".
Анализируем
Основой построения этого выражения является возможность разбить CSS селектор на части. Я разбил его так:
- где будем искать (среди детей 1 поколения, соседей или же во всем дереве, то есть + > ~, _пробел_)
- псевдо селектор, вида ":some_function(some_arguments)"
- селектор по атрибутам, вида "[someAttr(some expression like =)someValue]"
Сопоставим теперь все это с выражением.
Первая часть ищет или "+", ">", ",", "~", "\s+", если не находит, тогда ищем все что между ними.
Вторая — обрабатывает квадратные скобки. Шаблон "[^=]+=~?\s*" был построен для того, чтобы можно было делать поиск по атрибут селекторам, с помощью сколь угодно сложных регулярных выражений.
Третья — находит совпадения для псевдо селектора, при чем круглые скобки ставить не обязательно.
Все символы можно экранировать с помощью обратного слэша ("\") или же взять выражение в одинарные или же двойные кавычки, тогда они не будут восприняты как управляющие.
Заключение
Думаю дальше уже понятно насколько просто написать парсер CSS3 селекторов. Кому интересно поэкспериментировать — идите сюда. Буду очень благодарен, если кто-то выскажется на счет улучшения скорости роботы или строгости регулярного выражения.
Ну и конечно же, большое спасибо Дж. Фридлу, автору серии бесценных книг по регулярным выражениям
P.S.: извиняюсь за бажность анализатора регулярных выражений. Он был создан как промежуточный этап (работает в Chrome и FF точно). Если что-то не работает, там стоит callback на событие change, нажимайте на checkbox-и или просто вставте пробел в поле с регулярным выражением, должно запустится.