Регулярные выражения (regexp) — основы |
05.04.2021 00:00 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Автор: Ольга (Киселева) Назина Регулярные выражения (их еще называют regexp, или regex) — это механизм для поиска и замены текста. В строке, файле, нескольких файлах... Их используют разработчики в коде приложения, тестировщики в автотестах, да просто при работе в командной строке! Чем это лучше простого поиска? Тем, что позволяет задать шаблон. Например, на вход приходит дата рождения в формате ДД.ММ.ГГГГГ. Вам надо передать ее дальше, но уже в формате ГГГГ-ММ-ДД. Как это сделать с помощью простого поиска? Вы же не знаете заранее, какая именно дата будет. А регулярное выражение позволяет задать шаблон «найди мне цифры в таком-то формате». Для чего применяют регулярные выражения?
А еще для замены — например, чтобы изменить формат всех дат в файле. Если дата одна, можно изменить вручную. А если их 200, проще написать регулярку и подменить автоматически. Тем более что регулярные выражения поддерживаются даже простым блокнотом (в Notepad++ они точно есть). В этой статье я расскажу о том, как применять регулярные выражения для поиска и замены. Разберем все основные варианты. СодержаниеГде пощупатьЛюбое регулярное выражение из статьи вы можете сразу пощупать. Так будет понятнее, о чем речь в статье — вставили пример из статьи, потом поигрались сами, делая шаг влево, шаг вправо. Где тренироваться:
Инструменты есть, теперь начнём Поиск текстаСамый простой вариант регэкспа. Работает как простой поиск — ищет точно такую же строку, как вы ввели.
Выделение курсивом не поможет моментально ухватить суть, что именно нашел regex, а выделить цветом в статье я не могу. Атрибут BACKGROUND-COLOR не сработал, поэтому я буду дублировать регулярки текстом (чтобы можно было скопировать себе) и рисунком, чтобы показать, что именно regex нашел: Обратите внимание, нашлось именно «море», а не первое «Море». Регулярные выражения регистрозависимые! Хотя, конечно, есть варианты. В JavaScript можно указать дополнительный флажок i, чтобы не учитывать регистр при поиске. В блокноте (notepad++) тоже есть галка «Match case». Но учтите, что это не функция по умолчанию. И всегда стоит проверить, регистрозависимая ваша реализация поиска, или нет. А что будет, если у нас несколько вхождений искомого слова?
По умолчанию большинство механизмов обработки регэкспа вернет только первое вхождение. В JavaScript есть флаг g (global), с ним можно получить массив, содержащий все вхождения. А что, если у нас искомое слово не само по себе, это часть слова? Регулярное выражение найдет его:
Это поведение по умолчанию. Для поиска это даже хорошо. Вот, допустим, я помню, что недавно в чате коллега рассказывала какую-то историю про интересный баг в игре. Что-то там связанное с кораблем... Но что именно? Уже не помню. Как найти? Если поиск работает только по точному совпадению, мне придется перебирать все падежи для слова «корабль». А если он работает по включению, я просто не буду писать окончание, и все равно найду нужный текст:
Это статический, заранее заданный текст. Но его можно найти и без регулярок. Регулярные выражения особенно хороши, когда мы не знаем точно, что мы ищем. Мы знаем часть слова, или шаблон. Поиск любого символа. — найдет любой символ (один).
Точка найдет вообще любой символ, включая цифры, спецсисимволы, даже пробелы. Так что кроме нормальных имен, мы найдем и такие значения:
Учтите это при поиске! Точка очень удобный символ, но в то же время очень опасный — если используете ее, обязательно тестируйте получившееся регулярное выражение. Найдет ли оно то, что нужно? А лишнее не найдет? Точку точка тоже найдет!
Но что, если нам надо найти именно точку? Скажем, мы хотим найти все файлы с расширением txt и пишем такой шаблон:
Да, txt файлы мы нашли, но помимо них еще и «мусорные» значения, у которых слово «txt» идет в середине слова. Чтобы отсечь лишнее, мы можем использовать позицию внутри строки (о ней мы поговорим чуть дальше). Но если мы хотим найти именно точку, то нужно ее заэкранировать — то есть добавить перед ней обратный слеш:
Также мы будем поступать со всеми спецсимволами. Хотим найти именно такой символ в тексте? Добавляем перед ним обратный слеш. Правило поиска для точки:
Поиск по набору символовДопустим, мы хотим найти имена «Алла», «Анна» в списке. Можно попробовать поиск через точку, но кроме нормальных имен, вернется всякая фигня:
Если же мы хотим именно Анну да Аллу, вместо точки нужно использовать диапазон допустимых значений. Ставим квадратные скобки, а внутри них перечисляем нужные символы:
Вот теперь результат уже лучше! Да, нам все еще может вернуться «Анла», но такие ошибки исправим чуть позже. Как работают квадратные скобки? Внутри них мы указываем набор допустимых символов. Это может быть перечисление нужных букв, или указание диапазона:
Обратите внимание — если мы перечисляем возможные варианты, мы не ставим между ними разделителей! Ни пробел, ни запятую — ничего.
Единственный допустимый разделитель — это дефис. Если система видит дефис внутри квадратных скобок — значит, это диапазон:
Один символ! Не два или десять, а один! Учтите это, если захотите написать что-то типа [1-31]. Нет, это не диапазон от 1 до 31, эта запись читается так:
Здесь отсутствие разделителей играет злую шутку с нашим сознанием. Ведь кажется, что мы написали диапазон от 1 до 31! Но нет. Поэтому, если вы пишете регулярные выражения, очень важно их тестировать. Не зря же мы тестировщики! Проверьте то, что написали! Особенно, если с помощью регулярного выражения вы пытаетесь что-то удалить =)) Как бы не удалили лишнее... Указание диапазона вместо точки помогает отсеять заведомо плохие данные:
^ внутри [] означает исключение:
Например, мы хотим найти все txt файлы, кроме разбитых на кусочки — заканчивающихся на цифру:
Так как квадратные скобки являются спецсимволами, то их нельзя найти в тексте без экранирования:
Это регулярное выражение говорит «найди мне текст «fruits», а потом число 0». Квадратные скобки не экранированы — значит, внутри будет набор допустимых символов. Если мы хотим найти именно 0-левой элемент массива фруктов, надо записать так:
А если мы хотим найти все элементы массива фруктов, мы внутри экранированных квадратных скобок ставим неэкранированные!
Конечно, «читать» такое регулярное выражение становится немного тяжело, столько разных символов написано... Без паники! Если вы видите сложное регулярное выражение, то просто разберите его по частям. Помните про основу эффективного тайм-менеджмента? Слона надо есть по частям.
Разберем по частям регулярное выражение — fruits\[[0-9]\] Сначала идет просто текст — «fruits». Потом обратный слеш. Ага, он что-то экранирует. Что именно? Квадратную скобку. Значит, это просто квадратная скобка в моем тексте — «fruits[» Дальше снова квадратная скобка. Она не экранирована — значит, это набор допустимых значений. Ищем закрывающую квадратную скобку. Нашли. Наш набор: [0-9]. То есть любое число. Но одно. Там не может быть 10, 11 или 325, потому что квадратные скобки без квантификатора (о них мы поговорим чуть позже) заменяют ровно один символ. Пока получается: fruits[«любое однозназначное число» Дальше снова обратный слеш. То есть следующий за ним спецсимвол будет просто символом в моем тексте. А следующий символ — ] Получается выражение: fruits[«любое однозназначное число»] Наше выражение найдет значения массива фруктов! Не только нулевое, но и первое, и пятое... Вплоть до девятого:
Как найти вообще все значения массива, см дальше, в разделе «квантификаторы». А пока давайте посмотрим, как с помощью диапазонов можно найти все даты. Какой у даты шаблон? Мы рассмотрим ДД.ММ.ГГГГ:
Запишем в виде регулярного выражения: [0-9][0-9]\.[0-9][0-9]\.[0-9][0-9][0-9][0-9]. Напомню, что мы не можем записать диапазон [1-31]. Потому что это будет значить не «диапазон от 1 до 31», а «диапазон от 1 до 3, плюс число 1». Поэтому пишем шаблон для каждой цифры отдельно. В принципе, такое выражение найдет нам даты среди другого текста. Но что, если с помощью регулярки мы проверяем введенную пользователем дату? Подойдет ли такой regexp? Давайте его протестируем! Как насчет 8888 года или 99 месяца, а?
Попробуем ограничить:
Вот, уже лучше, явно плохие данные регулярка отсекла. Надо признать, она отсечет довольно много тестовых данных, ведь обычно, когда хотят именно сломать, то фигачат именно «9999» год или «99» месяц... Однако если мы присмотримся внимательнее к регулярному выражению, то сможем найти в нем дыры:
Мы не можем с помощью одного диапазона указать допустимые значения. Или мы потеряем 31 число, или пропустим 39. И если мы хотим сделать проверку даты, одних диапазонов будет мало. Нужна возможность перечислить варианты, о которой мы сейчас и поговорим. Перечисление вариантовКвадртатные скобки [] помогают перечислить варианты для одного символа. Если же мы хотим перечислить слова, то лучше использовать вертикальную черту — |.
Можно использовать вертикальную черту и для одного символа. Можно даже внутри слова — тогда вариативную букву берем в круглые скобки
Круглые скобки обозначают группу символов. В этой группе у нас или буква «н», или буква «л». Зачем нужны скобки? Показать, где начинается и заканчивается группа. Иначе вертикальная черта применится ко всем символам — мы будем искать или «Ан», или «ля»:
А если мы хотим именно «Аня» или «Аля», то перечисление используем только для второго символа. Для этого берем его в скобки. Эти 2 варианта вернут одно и то же:
Но для замены одной буквы лучше использовать [], так как сравнение с символьным классом выполняется проще, чем обработка группы с проверкой на все её возможные модификаторы. Давайте вернемся к задаче «проверить введенную пользователем дату с помощью регулярных выражений». Мы пробовали записать для дня диапазон [0-3][0-9], но он пропускает значения 33, 35, 39... Это нехорошо! Тогда распишем ТЗ подробнее. Та-а-а-ак... Если первая цифра:
Составим регулярные выражения на каждый пункт:
А теперь осталось их соединить в одно выражение! Получаем: 0[1-9]|[12][0-9]|3[01] По аналогии разбираем месяц и год. Но это остается вам для домашнего задания ? Потом, когда распишем регулярки отдельно для дня, месяца и года, собираем все вместе:
Обратите внимание — каждую часть регулярного выражения мы берем в скобки. Зачем? Чтобы показать системе, где заканчивается выбор. Вот смотрите, допустим, что для месяца и года у нас осталось выражение:
Подставим то, что написали для дня:
Как читается это выражение?
Видите проблему? Число «19» будет считаться корректной датой. Система не знает, что перебор вариантов | закончился на точке после дня. Чтобы она это поняла, нужно взять перебор в скобки. Как в математике, разделяем слагаемые. Так что запомните — если перебор идет в середине слова, его надо взять в круглые скобки!
Без скобок:
Итого, если мы хотим указать допустимые значения:
МетасимволыЕсли мы хотим найти число, то пишем диапазон [0-9]. Если букву, то [а-яА-ЯёЁa-zA-Z]. А есть ли другой способ? Есть! В регулярных выражениях используются специальные метасимволы, которые заменяют собой конкретный диапазон значений:
Это самые распространенные символы, которые вы будете использовать чаще всего. Но давайте разберемся с колонкой «эквивалентн». Для \d все понятно — это просто некие числа. А что такое «пробельные символы»? В них входят:
Из них вы чаще всего будете использовать сам пробел и перевод строки — выражение «\r\n». Напишем текст в несколько строк:
Для регулярного выражения это:
А вот что такое backspace в тексте? Как его можно увидеть вообще? Это же если написать символ и стереть его. В итоге символа нет! Неужели стирание хранится где-то в памяти? Но тогда это было бы ужасно, мы бы вообще ничего не смогли найти — откуда нам знать, сколько раз текст исправляли и в каких местах там теперь есть невидимый символ [\b]? Выдыхаем — этот символ не найдет все места исправления текста. Просто символ backspace — это ASCII символ, который может появляться в тексте (ASCII code 8, или 10 в octal). Вы можете «создать» его, написать в консоли браузера (там используется JavaScript):
Результат команды:
Мы написали «abc», а потом стерли «b» и «с». В итоге пользователь в консоли их не видит, но они есть. Потому что мы прямо в коде прописали символ удаления текста. Не просто удалили текст, а прописали этот символ. Вот такой символ регулярное выражение [\b] и найдет.
Но обычно, когда мы вводим \s, мы имеем в виду пробел, табуляцию, или перенос строки. Ок, с этими эквивалентами разобрались. А что значит [[:word:]]? Это один из способов заменить диапазон. Чтобы запомнить проще было, написали значения на английском, объединив символы в классы. Какие есть классы:
Теперь мы можем переписать регулярку для проверки даты, которая выберет лишь даты формата ДД.ММ.ГГГГГ, отсеяв при этом все остальное:
Согласитесь, через метасимволы запись посимпатичнее будет =)) СпецсимволыБольшинство символов в регулярном выражении представляют сами себя за исключением специальных символов:
Эти символы нужны, чтобы обозначить диапазон допустимых значений или границу фразы, указать количество повторений, или сделать что-то еще. В разных типах регулярных выражений этот набор различается (см «разновидности регулярных выражений»). Если вы хотите найти один из этих символов внутри вашего текста, его надо экранировать символом \ (обратная косая черта).
Можно экранировать целую последовательность символов, заключив её между \Q и \E (но не во всех разновидностях).
Квантификаторы (количество повторений)Усложняем задачу. Есть некий текст, нам нужно вычленить оттуда все email-адреса. Например:
Как составляется регулярное выражение? Нужно внимательно изучить данные, которые мы хотим получить на выходе, и составить по ним шаблон. В email два разделителя — собачка «@» и точка «.». Запишем ТЗ для регулярного выражения:
Так, до собачки у нас явно идет метасимвол «\w», туда попадет и просто текст (test), и цифры (olga31), и подчеркивание (pupsik_99). Но есть проблема — мы не знаем, сколько таких символов будет. Это при поиске даты все ясно — 2 цифры, 2 цифры, 4 цифры. А тут может быть как 2, так и 22 символа. И тут на помощь приходят квантификаторы — так называют специальные символы в регулярных выражениях, которые указывают количество повторений текста. Символ «+» означает «одно или более повторений», это как раз то, что нам надо! Получаем: \w+@ После собачки и снова идет \w, и снова от одного повторения. Получаем: \w+@\w+\. После точки обычно идут именно символы, но для простоты можно снова написано \w. И снова несколько символов ждем, не зная точно сколько. Итого получилось выражение, которое найдет нам email любой длины:
Какие есть квантификаторы, кроме знака «+»?
Символ * часто используют с точкой — когда нам неважно, какой идет текст до интересующей нас фразы, мы заменяем его на «.*» — любой символ ноль или более раз.
Но будьте осторожны! Если использовать «.*» повсеместно, можно получить много ложноположительных срабатываний:
Уж лучше \w, и плюсик вместо звездочки. А вот есть мы хотим найти все лог-файлы, которые нумеруются — log, log1, log2… log133, то * подойдет хорошо:
А знак вопроса (ноль или одно повторение) поможет нам найти людей с конкретной фамилией — причем всех, и мужчин, и женщин:
Если мы хотим применить квантификатор к группе символов или нескольким словам, их нужно взять в скобки:
Квантификаторы применяются к символу или группе в скобках, которые стоят перед ним. А что, если мне нужно определенное количество повторений? Скажем, я хочу записать регулярное выражение для даты. Пока мы знаем только вариант «перечислить нужный метасимвол нужное количество раз» — \d\d\.\d\d\.\d\d\d\d. Ну ладно 2-4 раза повторение идет, а если 10? А если повторить надо фразу? Так и писать ее 10 раз? Не слишком удобно. А использовать * нельзя:
Чтобы указать конкретное количество повторений, их надо записать внутри фигурных скобок:
Таким образом, для проверки даты можно использовать как перечисление \d n раз, так и использование квантификатора:
Обе записи будут валидны. Но вторая читается чуть проще — не надо самому считать повторения, просто смотрим на цифру. Не забывайте — квантификатор применяется к последнему символу! Regex: data{2} Найдет: dataa Не найдет: datadata Или группе символов, если они взяты в круглые скобки: Regex: (data){2} Найдет: datadata Не найдет: dataa Так как фигурные скобки используются в качестве указания количества повторений, то, если вы ищете именно фигурную скобку в тексте, ее надо экранировать:
Иногда квантификатор находит не совсем то, что нам нужно.
Мы хотим найти все теги HTML или XML по отдельности, а регулярное выражение возвращает целую строку, внутри которой есть несколько тегов. Напомню, что в разных реализациях регулярные выражения могут работать немного по разному. Это одно из отличий — в некоторых реализациях квантификаторам соответствует максимально длинная строка из возможных. Такие квантификаторы называют жадными. Если мы понимаем, что нашли не то, что хотели, можно пойти двумя путями:
Как учитывать символы? Для примера с тегами можно написать такое регулярное выражение:
Оно ищет открывающий тег, внутри которого все, что угодно, кроме закрывающегося тега «>», и только потом тег закрывается. Так мы не даем захватить лишнее. Но учтите, использование ленивых квантификаторов может повлечь за собой обратную проблему — когда выражению соответствует слишком короткая, в частности, пустая строка.
Есть еще и сверхжадная квантификация, также именуемая ревнивой. Но о ней почитайте в википедии =) Позиция внутри строкиПо умолчанию регулярные выражения ищут «по включению».
Это не всегда то, что нам нужно. Иногда мы хотим найти конкретное слово. Если мы ищем не одно слово, а некую строку, проблема решается в помощью пробелов:
Или так:
А что, если у нас не пробел рядом с искомым словом? Это может быть знак препинания: «И вот перед нами арка.», или «...арка:». Если мы ищем конкретное слово, то можно использовать метасимвол \b, обозначающий границу слова. Если поставить метасимвол с обоих концов слова, мы найдем именно это слово:
Можно ограничить только спереди — «найди все слова, которые начинаются на такое-то значение»:
Можно ограничить только сзади — «найди все слова, которые заканчиваются на такое-то значение»:
Если использовать метасимвол \B, он найдем нам НЕ-границу слова:
Если мы хотим найти конкретную фразу, а не слово, то используем следующие спецсимволы:
Если использовать их, мы будем уверены, что в наш текст не закралось ничего лишнего:
Итого метасимволы, обозначающие позицию строки:
Использование ссылки назадДопустим, при тестировании приложения вы обнаружили забавный баг в тексте — дублирование предлога «на»: «Поздравляем! Вы прошли на на новый уровень». А потом решили проверить, есть ли в коде еще такие ошибки. Разработчик предоставил файлик со всеми текстами. Как найти повторы? С помощью ссылки назад. Когда мы берем что-то в круглые скобки внутри регулярного выражения, мы создаем группу. Каждой группе присваивается номер, по которому к ней можно обратиться.
Разберемся, что означает это регулярное выражение:
Важно: синтаксис ссылок назад очень зависит от реализации регулярных выражений.
Для чего еще нужна ссылка назад? Например, можно проверить верстку HTML, правильно ли ее составили? Верно ли, что открывающийся тег равен закрывающемуся? Напишите выражение, которое найдет правильно написанные теги:
Но не найдет ошибки:
Просмотр вперед и назадЕще может возникнуть необходимость найти какое-то место в тексте, но не включая найденное слово в выборку. Для этого мы «просматриваем» окружающий текст.
ЗаменаВажная функция регулярных выражений — не только найти текст, но и заменить его на другой текст! Простейший вариант замены — слово на слово:
Но что, если у нас в исходном тексте может быть любое имя? Вот что пользователь ввел, то и сохранилось. А нам надо на Макара теперь заменить. Как сделать такую замену? Через знак доллара. Давайте разберемся с ним подробнее. Знак доллара в замене — обращение к группе в поиске. Ставим знак доллара и номер группы. Группа — это то, что мы взяли в круглые скобки. Нумерация у групп начинается с 1.
Мы искали фразу «Оля + Маша» (круглые скобки не экранированы, значит, в искомом тексте их быть не должно, это просто группа). А замнили ее на первую группу — то, что написано в первых круглых скобках, то есть текст «Оля». Это работает и когда искомый текст находится внутри другого:
Можно каждую часть текста взять в круглые скобки, а потом варьировать и менять местами:
Теперь вернемся к нашей задаче — есть строка приветствия «Привет, кто-то там!», где может быть написано любое имя (даже просто числа вместо имени). Мы это имя хотим заменить на «Макар». Нам надо оставить текст вокруг имени, поэтому берем его в скобки в регулярном выражении, составляя группы. И переиспользуем в замене:
Давайте разберемся, как работает это регулярное выражение. ^ — начало строки. Дальше скобка. Она не экранирована — значит, это группа. Группа 1. Поищем для нее закрывающую скобку и посмотрим, что входит в эту группу. Внутри группы текст «Привет, » После группы идет выражение «.*» — ноль или больше повторений чего угодно. То есть вообще любой текст. Или пустота, она в регулярку тоже входит. Потом снова открывающаяся скобка. Она не экранирована — ага, значит, это вторая группа. Что внутри? Внутри простой текст — «!». И потом символ $ — конец строки. Посмотрим, что у нас в замене. $1 — значение группы 1. То есть текст «Привет, ». Макар — просто текст. Обратите внимание, что мы или включает пробел после запятой в группу 1, или ставим его в замене после «$1», иначе на выходе получим «Привет,Макар». $2 — значение группы 2, то есть текст «!» Вот и всё! А что, если нам надо переформатировать даты? Есть даты в формате ДД.ММ.ГГГГ, а нам нужно поменять формат на ГГГГ-ММ-ДД. Регулярное выражение для поиска у нас уже есть — «\d{2}\.\d{2}\.\d{4}». Осталось понять, как написать замену. Посмотрим внимательно на ТЗ:
По нему сразу понятно, что нам надо выделить три группы. Получается так: (\d{2})\.(\d{2})\.(\d{4}) В результате у нас сначала идет год — это третья группа. Пишем: $3 Потом идет дефис, это просто текст: $3- Потом идет месяц. Это вторая группа, то есть «$2». Получается: $3-$2 Потом снова дефис, просто текст: $3-$2- И, наконец, день. Это первая группа, $1. Получается: $3-$2-$1 Вот и всё!
Другой пример — я записываю в блокнот то, что успела сделать за цикл в 12 недель. Называется файлик «done», он очень мотивирует! Если просто вспоминать «что же я сделал?», вспоминается мало. А тут записал и любуешься списком. Вот пример улучшалок по моему курсу для тестировщиков:
И таких набирается штук 10-25. За один цикл. А за год сколько? Ух! Вроде небольшие улучшения, а набирается прилично. Так вот, когда цикл заканчивается, я пишу в блог о своих успехах. Чтобы вставить список в блог, мне надо удалить нумерацию — тогда я сделаю ее силами блоггера и это будет смотреться симпатичнее. Удаляю с помощью регулярного выражения:
Можно было бы и вручную. Но для списка больше 5 элементов это дико скучно и уныло. А так нажал одну кнопочку в блокноте — и готово! Так что регулярные выражения могут помочь даже при написании статьи =) Статьи и книги по темеКниги Регулярные выражения 10 минут на урок. Бен Форта — Очень рекомендую! Прям шикарная книга, где все просто, доступно, понятно. Стоит 100 рублей, а пользы море.
Статьи Вики — https://ru.wikipedia.org/wiki/Регулярные_выражения. Да, именно ее вы будете читать чаще всего. Я сама не помню наизусть все метасимволы. Поэтому, когда использую регулярки, гуглю их, википедия всегда в топе результатов. А сама статья хорошая, с табличками удобными. Регулярные выражения для новичков — https://tproger.ru/articles/regexp-for-beginners/ ИтогоРегулярные выражения — очень полезная вещь для тестировщика. Применений у них много, даже если вы не автоматизатор и не спешите им стать:
Если вы знаете, что в коде вашей программы есть регулярное выражение, вы можете его протестировать. Вы также можете использовать регулярки внутри ваших автотестов. Хотя тут стоит быть осторожным. Не забывайте о шутке: «У разработчика была одна проблема и он стал решать ее с помощью регулярных выражений. Теперь у него две проблемы». Бывает и так, безусловно. Как и с любым другим кодом. Поэтому, если вы пишете регулярку, обязательно ее протестируйте! Особенно, если вы ее пишете в паре с командой rm (удаление файлов в linux). Сначала проверьте, правильно ли отрабатывает поиск, а потом уже удаляйте то, что нашли. Регулярное выражение может не найти то, что вы ожидали. Или найти что-то лишнее. Особенно если у вас идет цепочка регулярок. Думаете, это так легко — правильно написать регулярку? Попробуйте тогда решить задачку от Егора или вот эти кроссворды =) PS — больше полезных статей ищите в моем блоге по метке «полезное». А полезные видео — на моем youtube-канале |