[Урок 6] . Регулярные выражения в Javascript. Основы.

Регулярные выражения — это своего рода язык поиска и осуществления различных манипуляций с текстом, используя различные мета-символы. Регулярные выражения позволяют создавать шаблоны для поиска подстрок, удовлетворяющим заданным условиям. Подобный функционал имеется на вооружении большинства языков программирования, и Javascript — один из таких языков.

Область применения регулярных выражений

В прошлом уроке мы разбирали работу со строками и там было несколько функций для поиска и извлечения искомых подстрок. Может возникнуть вопрос, а зачем придумывать что-то еще, если у нас уже есть достаточно богатый функционал для раздербанивания и анализа строк? Отвечаю, при работе с огромным количество текста, особенно, который генерируется динамически, можно проследить некоторые паттерны(повторяющиеся фрагменты и структура текста в-целом). Допустим, нам нужно выдрать из таблички все названия товаров и цены, а сколько данных будет в таблице — нам не известно. Можно в цикле использовать indexOf() и substring(), но код получится громоздким и не очень надежным. Регулярные выражения очень удобно использовать для валидации данных, например для электронной почты, номера телефона, даты и т.д.

Помните задачку из прошлого урока «Определение баланса биткойн-крана»? Там мы находили определенный тэг и из него извлекали текст, после чего получали оттуда цифру. Если мы попытались бы использовать тот же скрипт на другом кране — не факт, что он заработал бы, поскольку сведения о балансе могли находится в совершенно другом тэге с другим классом и другими атрибутами.

При помощи регулярных выражений мы можем сделать универсальный скрипт, который будет искать баланс крана не по тэгу, а в тексте страницы.

Алгоритм такой:

  1. Мы получаем содержимое всей страницы в текстовом формате
  2. Составляем паттерн на основе строки Balance: 145335 satoshi. Логически это выглядит примерно так [Balance:][пробел или другие разделители][последовательность из 1-9 цифр][пробел или другие разделители][ satoshi]
  3. Ищем в тексте все совпадения с нашим паттерном и при успехе, вытаскиваем значение из 1-9 цифр.

Выглядеть это будет так:

var str = window.document.querySelector('body').textContent;
var re = /Balance:\s{0,}(\d{1,9})\s{0,}satoshi/igm;
var result = re.exec(str);

window.console.log(result); // ["Balance: 145335 satoshi", "145335"]
window.console.log(result[1]); // 145335 - это баланс нашего крана

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

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

Для тестирования и написания паттернов в режиме онлайн я обычно использую сервис https://regex101.com. Выбираете там Javascript и смотрите в риалтайме, как обрабатывается текст вашей регуляркой, плюс там есть подсказки и небольшой справочник. 

Есть несколько способов задания регулярного выражения. Вот пример синтаксиса:

// Стандартный метод
var re = new RegExp("паттерн", "флаги");

// Укороченная форма записи
var re = /паттерн/; // без флагов
var re = /паттерн/gmi; // с флагами gmi

Флаги — это параметры поиска, их всего несколько видов и вы можете использовать любой из них, или даже все сразу.
i ignore case, Если этот флаг есть, то регэксп ищет независимо от регистра, то есть не различает между А и а.
gglobal match, Если этот флаг есть, то регэксп ищет все совпадения, иначе – только первое.
mmultiline, Многострочный режим.

Пример использования:

var str = 'Писать ботов на iMacros+JS очень круто!';

window.console.log(/imacros/i.test(str)); // true
window.console.log(/imacros/.test(str)); // false

Методы Javascript для работы с регулярными выражениями

В Javascript Существует 6 методов для работы с регулярными выражениями. Чаще всего мы будем использовать только половину из них.

Метод exec()

Метод RegExp, который выполняет поиск совпадения в строке. Он возвращает массив данных. Например:

var str = 'Some fruit: Banana - 5 pieces. For 15 monkeys.';

var re = /(\w+) - (\d) pieces/ig;
var result = re.exec(str);

window.console.log(result);

// result = ["Banana - 5 pieces", "Banana", "5"]
// Так же мы можем посмотреть позицию совпадения - result.index

В результате мы получим массив, первым элементом которого будет вся совпавшая по паттерну строка, а дальше содержимое скобочных групп. Если совпадений с паттерном нету, то result = null.

Метод test()

Метод RegExp, который проверяет совпадение в строке, возвращает либо true, либо false. Очень удобен, когда нам необходимо проверить наличие или отсутствие паттерна в тексте. Например:

var str = 'Balance: 145335 satoshi';

var re = /Balance:/ig;
var result = re.test(str);

window.console.log(result); // true

В данном примере, есть совпадение с паттерном, поэтому получаем true.

Метод search()

Метод String, который тестирует на совпадение в строке. Он возвращет индекс совпадения, или -1 если совпадений не будет найдено. Очень похож на метод indexOf() для работы со строками. Минус этого метода — он ищет только первое совпадение. Для поиска всех совпадений используйте метод match().

var str = "Умея автоматизировать процессы, можно зарабатывать миллионы";
window.console.log(str.search(/можно/igm)); // 60 
window.console.log(str.search(/атата/igm)); // -1

Метод match()

Метод String, который выполняет поиск совпадения в строке. Он возвращет массив данных либо null если совпадения отсутствуют.

// Без использования скобочных групп
var str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
var regexp = /[A]/gi;
var matches_array = str.match(regexp);

window.console.log(matches_array); // ["A", "a"]

// С использованием скобочных групп без флага g
var str = 'Fruits quantity: Apple - 5, Banana - 7, Orange - 12. I like fruits.';

var found = str.match(/(\d{1,2})/i);
window.console.log(found); // Находит первое совпадение и возвращает объект
// {
// 0:		"5"	
// 1:		"5"	
// index:	25	
// input:	"Fruits quantity: Apple -...ge - 12. I like fruits."
// }

// С использованием скобочных групп с флагом g
var found = str.match(/(\d{1,2})/igm);
window.console.log(found); // ["5", "7", "12"]

Если совпадений нету — возвращает null.

Метод replace()

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

// Обычная замена
var str = 'iMacros is awesome, and iMacros is give me profit!';
var newstr = str.replace(/iMacros/gi, 'Javascript');

window.console.log(newstr); // Javascript is awesome, and Javascript is give me profit!

// Замена, используя параметры. Меняем слова местами:
var re = /(\w+)\s(\w+)/;
var str = 'iMacros JS';
var newstr = str.replace(re, '$2, $1'); // в переменных $1 и $2 находятся значения из скобочных групп
window.console.log(newstr); // JS iMacros

У метода replace() есть очень важная особенность — он имеет свой каллбэк. То есть, в качестве аргумента мы можем подавать функцию, которая будет обрабатывать каждое найденное совпадение. 

Нестандартное применение метода replace():

var str = `
	I have some fruits:
	Orange - 5 pieces
	Banana - 7 pieces
	Apple - 15 pieces
	It's all.
`;
var arr = []; // Сюда складируем данные о фруктах и их количестве

var newString = str.replace(/(\w+) - (\d) pieces/igm, function (match, p1, p2, offset, string) {
	
	window.console.log(arguments);
	
	arr.push({
		name: p1,
		quantity: p2
	});

	return match;
});


window.console.log(newString);  // Текст не изменился, как и было задумано
window.console.log(arr);        // Мы получили удобный массив объектов, с которым легко и приятно работать

Как вы видите, мы использовали этот метод для обработки каждого совпадения. Мы вытащили из паттерна название фрукта и количество и поместили эти значения в массив объектов, как мы уже делали ранее. Обратите внимание на аргумент функции offset — это будет индекс начала совпадения, этот параметр нам потом пригодится. В нашем случае, мы имеем 2 скобочные группы в паттерне, поэтому у нас в функции 5 аргументов, но их там может быть и больше.

Метод split()

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

var str = "08-11-2016";

// Разбиваем строку по разделителю
window.console.log(str.split('-')); // ["08", "11", "2016"]

// Такой же пример с регэкспом
window.console.log(str.split(/-/)); // ["08", "11", "2016"]

Составление регулярных выражений

Для поиска символов определенного вида в регулярных выражениях есть «классы символов». Например \d ищет одну цифру. Есть и другие классы.

Основные классы

  • \d – цифры.
  • \D – не-цифры.
  • \s – пробельные символы, переводы строки.
  • \S – всё, кроме \s.
  • \w – латиница, цифры, подчёркивание ‘_’.
  • \W – всё, кроме \w.
  • \t – символ табуляции
  • \n – символ перевода строки
  • . – точка обозначает любой символ, кроме перевода строки.

Помимо классов, есть наборы символов, причем помимо стандартных, можно самому создавать набор символов, используя [].

Наборы символов

  • [a-z] – произвольный символ от a до z
  • [B-G] – произвольный символ от B до G в верхнем регистре
  • [0-9] – то же самое, что и \d, любая цифра от 0 до 9
  • [a-zA-Z0-9] – любая цифра или буква, можно комбинировать диапазоны
  • [^0-9] – диапазон «кроме», ищет любые символы, кроме цифр

Обратите внимание, в квадратных скобках, большинство специальных символов можно использовать без экранирования.

Экранируем символы

[ \ ^ $ . | ? * + ( )

Не экранируем в квадратных скобках

  • Точка '.'.
  • Плюс '+'.
  • Круглые скобки '( )'.
  • Дефис '-', если он находится в начале или конце квадратных скобок, то есть не выделяет диапазон.
  • Символ каретки '^', если не находится в начале квадратных скобок.
  • А также открывающая квадратная скобка '['.

Квантификаторы +, *, ? и {n}

  • Количество {n}
    \d{5} — 5 цифр
    \d{1,} — одна или больше цифр
    \d{1,5} — от одной до 5 цифр
  • Один или более +, аналог {1,}
    \d+ — одна или более цифр
    [a-z]+ — одна или более букв от a до z
  • Ноль или один ?, аналог {0,1}
    \colo?r\ — найдет color и colour
  • Означает «ноль или более» *, аналог {0,}
    \d0* — найдет «1» и «100» в строке «1 100», символ может повторяться много раз или вообще отсутствовать.

Скобочные группы

Вы можете заключить одну или несколько частей шаблона в скобки — это и будет называться скобочной группой. Основной профит — при использовании методов match, exec, replace — можно вытащить значения в скобках в отдельный элемент массива. И еще, если поставить квантификатор после скобки, то он применится ко всей скобке, а не к одному символу. Еще вы можете использовать вложенные скобки, то есть разбивать большой шаблон на несколько маленьких.

Пример использования вы можете посмотреть выше — в нестандартном применении метода replace().

Альтернация

Простым языком, это «ИЛИ».

/0|1|2|3|4|5|6|7|8|9/ — аналог \d  -любая цифра
/imacros|js/ — найдет «imacros» или «js»

Начало строки ^ и конец $

Знак каретки ‘^’ и доллара ‘$’ имеют в регулярном выражении особый смысл. Их называют «якорями» (anchor – англ.).
Каретка ^ совпадает в начале текста, а доллар $ – в конце. Знак доллара $ используют, чтобы указать, что паттерн должен заканчиваться в конце текста, знак каретки ^ — что паттерн должен начинаться с первого символа строки.

Подытожим

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

Оставляйте ваши пожелания, вопросы и комментарии!

15 комментариев

  1. Доброго времени! Ну очень хорошо разжевываете! Ставлю на 1е место по простоте изложения.
    У меня задачка — спарсить последний № телефона со страницы, он записан в виде +79851234567 — тут плюсик и 11 цифр
    window.console.log(window.document.querySelectorAll(«‘+’\d{11}») );
    не получается. Подскажите, как должно выглядеть выражение.

    Ответить
    • Александр, регулярное выражение вы правильное составили, но метод window.document.querySelectorAll() принимает в качестве аргумента CSS-селектор(регулярки не поддерживает).

      Навскидку вижу два варианта:

      1) Вы получаете текст всей страницы и регулярками ищете номер телефона.
      var str = window.document.querySelector('body').textContent;
      var result = /\d{1,11}/igm.exec(str);
      window.console.log(result[1]);

      2) При помощи window.document.querySelector() ищете тэг, в котором содержится номер телефона и оттуда уже регулярками вытаскиваете цифры.

      Ответить
      • Использовал 1-й вариант, поскольку вытаскивать тлф.нужно было из таблицы, сделал так.
        var str = window.document.querySelector(‘tbody’).textContent;
        var result = /[+]\d{1,11}/igm.exec(str);
        Из полного body как ни бился, не получилось.
        Только с добавлением плюсика выбирался только ненужный 1й телефон.

        Ответить
        • Александр, используйте «Нестандартное применение метода replace():» из статьи. Так вы сможете выдрать все телефоны на странице и выбрать нужный, например по индексу. Но я рекомендую использовать второй способ, он более надежный и точный.

          Ответить
          • Спасибо, меня устраивает щас в виде
            var koshel = /\d{11}QIWI/igm.exec(str); //79131234567QIWI

  2. Киви
    +79131238628
    QIWI
    Верифицирован
    Это инспектирование элемента при помощи Firebug

    Ответить
  3. Здравствуйте!!!!!!
    не подскажите
    if(переменная1.indexOf(«переменная2») + 1)
    почему у меня не работает с переменная2?
    а если просто слово пишешь «забор» то все работает?

    Ответить
    • Алексей, во-первых приводите реальный код или вы действительно используете переменные на кириллице?
      Во-вторых, я вижу отсутствие кавычек у первой переменной и наличие у второй, если это кусок реального кода, то уберите кавычки для второй переменной.
      В-третьих, вы используете сокращенный вариант условия if, который подразумевает, что если условие или переменная будет true — то код после фигурных скобок выполнится.
      Я вижу, вы используете прибавление «+1» для подгонки результата, но это не верно, метод indexOf() работает немного иначе:

      // indexOf() - возвращает номер первого совпавшего символа или -1
      window.console.log( "test123phrase".indexOf("123") ); //  4, есть совпадение в середине
      window.console.log( "123testphrase".indexOf("123") ); //  0, есть совпадение с первого символа
      window.console.log( "testphrase".indexOf("123") );    // -1, нет совпадений
      
      // Сравнение переменных и приведение к типу
      window.console.log( 1 == true );   // true
      window.console.log( 0 == false );  // true
      window.console.log( 0 == false );  // true
      
      window.console.log( -1 == true );  // false
      window.console.log( -1 == false ); // false
      
      // Универсальное преобразование при работе с indexOf()
      // ~ - побитовое "НЕ" + преобразование("!") к булеву типу + обычное "НЕ" ("!"), которое меняет полярность из false в true
      // 
      // "testphra123se".indexOf("123")    // =  8
      // ~"testphra123se".indexOf("123")   // = -9
      // !~"testphra123se".indexOf("123")  // = false
      // !!~"testphra123se".indexOf("123") // = true
      
      if (!!~"123testphrase".indexOf("123")) {
      	// Если есть совпадения
      	window.console.log('found.');
      } else {
      	//Если нет совпадений
      	window.console.log('not found.');
      }

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

      Ответить
      • спасибо большое за разъяснения!!!
        «затупил» с кавычками
        ****************************************************
        var zap1 =(‘кошка. собака’)
        var zap = zap1.replace(‘собака’,»); // удалит содержимое кавычек (‘собака’) в переменной
        ****************************************************
        а можно удалять не конкретный текст а все что после «.»

        Ответить
  4. Подскажите пожалуйста в чем может быть проблемма??
    var paRS =(‘Мартынова’)
    var paRSS =(‘Мартынова’)
    if(paRS.indexOf(paRSS) ){c(60,’WAIT SECONDS=1′);}
    else {c(60,’WAIT SECONDS=2′);}
    выдаёт 2 сек
    почему????
    проблема именно с этим словом!!
    что можно сделать??

    Ответить
  5. не могу придумать регулрное выражение
    [CODE]
    //—— из строковой переменной возвращает значение атрибута
    function getAttr(vStr,vAttr) {
    //
    var re = /(vAttr + ‘=\»‘ «)/igm; //не знаю как задать выражение
    var result = re.exec(vStr);
    return result;
    };
    //——- end function ————
    var vUrl = ‘✰ DeViL^NeKo ✰ | Аниме Ecchi | hentai‘;
    window.console.log(getAttr(vUrl,’href’));
    //хочу вернуть — «/devil_neko_new3»
    [/CODE]

    Ответить
  6. выкрутился без регулырных..
    //====== функция =========
    // из строковой переменной возвращает значение атрибута
    // типа так
    // vUrl = ‘? DeViL^NeKo ? | Аниме Ecchi | hentai‘;
    // window.console.log(getAttr(vUrl,’href’);
    // ———
    function getAttr(vStr,vAttr) {
    //
    var result = «»;
    var start = vStr.indexOf(vAttr) + 2 + vAttr.length; //
    //
    for (var n = start; n < vStr.length; n++) {
    if (vStr.charAt(n) == '"') {
    break; // если дошли до " то закончить
    };
    result += vStr.charAt(n);
    };
    return result;
    };
    //======== end function =========

    Ответить

Оставить комментарий