Регулярные выражения javascript как разрешить пустую строку. Выразительный JavaScript: Регулярные выражения

Регулярные выражения (RegExp ) - это очень эффективный способ работы со строками.

Составив регулярное выражение с помощью специального синтаксиса вы можете:

  • искать текст в строке
  • заменять подстроки в строке
  • извлекать информацию из строки

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

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

Регулярные выражения реализованные в UNIX, таких как grep, sed и популярных текстовых редакторах, начали набирать популярность и были добавлены в язык программирования Perl, а позже и в множество других языков.

JavaScript, наряду с Perl, это один из языков программирования в котором поддержка регулярных выражений встроена непосредственно в язык.

Сложно, по полезно

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

Регулярные выражения сложно писать , сложно читать и сложно поддерживать/изменять .

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

Это руководство нацелено на то чтобы самым простым способом дать вам некоторое представление о регулярных выражениях в JavaScript и предоставить информацию о том как читать и создавать регулярные выражения.

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

Как выглядят регулярные выражения

В JavaScript регулярные выражения это объект, который может быть определён двумя способами.

Первый способ заключается в создании нового объекта RegExp с помощью конструктора:

Const re1 = new RegExp("hey")

Второй способ заключается в использовании литералов регулярных выражений :

Const re1 = /hey/

Вы знаете что в JavaScript есть литералы объектов и литералы массивов ? В нём также есть литералы regexp .

В приведённом выше примере hey называется шаблоном . В литеральной форме он находится между двумя слэшами, а в случае с конструктором объекта, нет.

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

Как они работают?

Регулярное выражение, которое мы определили выше как re1 , очень простое. Оно ищет строку hey без каки-либо ограничений: строка может содержать много текста, а слово hey находиться где-то в середине и регулярное выражение сработает. Строка может содержать только слово hey и регулярка опять сработает.

Это довольно просто.

Вы можете попробовать протестировать регулярное выражение с помощью метода RegExp.test(String) , который возвращает логическое (boolean) значение:

Re1.test("hey") // ✅ re1.test("blablabla hey blablabla") // ✅ re1.test("he") // ❌ re1.test("blablabla") // ❌

В примере выше мы просто проверили удовлетворяет ли "hey" шаблону регулярного выражения, который храниться в re1 .

Это проще простого, но вы уже знаете много о регулярных выражениях.

Закрепление /hey/

сработает независимо от того где находится hey внутри строки.

Если вы хотите найти строки, которые начинаются с hey , то используйте оператор ^ :

/^hey/.test("hey") // ✅ /^hey/.test("bla hey") // ❌

Если вы хотите найти строки, которые заканчиваются на hey , то используйте оператор $ :

/hey$/.test("hey") // ✅ /hey$/.test("bla hey") // ✅ /hey$/.test("hey you") // ❌

Объединяя два предыдущих оператора вы можете найти строку, которая полностью совпадает с hey:

/^hey$/.test("hey") // ✅

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

/^hey.*joe$/.test("hey joe") // ✅ /^hey.*joe$/.test("heyjoe") // ✅ /^hey.*joe$/.test("hey how are you joe") // ✅ /^hey.*joe$/.test("hey joe!") // ❌

Поиск элементов по диапазону

Вместо того чтобы искать определённую строку, вы можете указать диапазон символов, например:

// // a, b, c, ... , x, y, z // // A, B, C, ... , X, Y, Z // // a, b, c // // 0, 1, 2, 3, ... , 8, 9

Эти регулярные выражения ищут строки, которые содержат хотя бы один символ из выбранного диапазона:

//.test("a") // ✅ //.test("1") // ❌ //.test("A") // ❌ //.test("d") // ❌ //.test("dc") // ✅

Диапазоны можно комбинировать:

// //.test("a") // ✅ //.test("1") // ✅ //.test("A") // ✅

Поиск многократных совпадений элемента диапазона

Вы можете проверить содержит ли строка только один символ из диапазона с помощью символа - :

/^$/ /^$/.test("A") // ✅ /^$/.test("Ab") // ❌

Инверсия шаблона

Символ ^ в начале шаблона привязывает его к началу строки.

Использование этого символа внутри диапазона инвертирует диапазон, поэтому:

/[^A-Za-z0-9]/.test("a") // ❌ /[^A-Za-z0-9]/.test("1") // ❌ /[^A-Za-z0-9]/.test("A") // ❌ /[^A-Za-z0-9]/.test("@") // ✅

Метасимволы
  • \d совпадает с любым числом, эквивалентно
  • \D совпадает с любым символом, который не является числом, эквивалентно [^0-9]
  • \w совпадает с любым буквенно-числовым символом, эквивалентно
  • \W совпадает с любым символом, который не является буквенно-числовым значением, эквивалентно [^A-Za-z0-9]
  • \s совпадает с любым пробельным символом: пробел, табуляция, символ новой строки и пробелы Unicode
  • \S совпадает с любым символом, который не является пробелом
  • \0 совпадает с null
  • \n совпадает с символом новой строки
  • \t совпадает с символом табуляции
  • \uXXXX совпадает с символом Unicode с кодом XXXX (требуется флаг u)
  • . совпадает с любым символовом, кроме символа новой строки (таким как \n) (если вы не используете флаг s , объясним позже)
  • [^] совпадает с любым символом, включая символ новой строки. Полезно при работе с многострочными строками
Выбор в регулярных выражениях

Если вы хотите выбрать одну или другую строку, используйте оператор | .

/hey|ho/.test("hey") // ✅ /hey|ho/.test("ho") // ✅

Квантификаторы

Представьте что у вас есть регулярное выражение, которое проверяет строку на то чтобы она состояла только из одной цифры:

Вы можете использовать квантификатор ? , который сделает этот символ необязательным. В нашем случае цифра должна встречаться 0 или 1 раз:

но что если мы хотим чтобы регулярное выражение срабатывало на несколько цифр?

Вы можете сделать это 4 способами, используя + , * , {n} и {n,m} .

+

Совпадает с одним или более (>=1) элементами:

/^\d+$/ /^\d+$/.test("12") // ✅ /^\d+$/.test("14") // ✅ /^\d+$/.test("144343") // ✅ /^\d+$/.test("") // ❌ /^\d+$/.test("1a") // ❌

*

Совпадает с 0 или более (>=0) элементами:

/^\d+$/ /^\d*$/.test("12") // ✅ /^\d*$/.test("14") // ✅ /^\d*$/.test("144343") // ✅ /^\d*$/.test("") // ✅ /^\d*$/.test("1a") // ❌

{n}

Совпадает точно с n количеством элементов:

/^\d{3}$/ /^\d{3}$/.test("123") // ✅ /^\d{3}$/.test("12") // ❌ /^\d{3}$/.test("1234") // ❌ /^{3}$/.test("Abc") // ✅

{n,m}

Совпадает с диапазоном от n до m элементов:

/^\d{3,5}$/ /^\d{3,5}$/.test("123") // ✅ /^\d{3,5}$/.test("1234") // ✅ /^\d{3,5}$/.test("12345") // ✅ /^\d{3,5}$/.test("123456") // ❌

m можно опустить и оставить второй предел без ограничений, чтобы было минимум n элементов:

/^\d{3,}$/ /^\d{3,}$/.test("12") // ❌ /^\d{3,}$/.test("123") // ✅ /^\d{3,}$/.test("12345") // ✅ /^\d{3,}$/.test("123456789") // ✅

Опциональные элементы

Следующий за элементом знак? , сделает его необязательным:

/^\d{3}\w?$/ /^\d{3}\w?$/.test("123") // ✅ /^\d{3}\w?$/.test("123a") // ✅ /^\d{3}\w?$/.test("123ab") // ❌

Группы

Используя круглые скобки, вы можете создавать группы символов (...) .

Пример ниже ищет точное совпадение из 3 цифр за которым следует один или более буквенно-числовые символов:

/^(\d{3})(\w+)$/ /^(\d{3})(\w+)$/.test("123") // ❌ /^(\d{3})(\w+)$/.test("123s") // ✅ /^(\d{3})(\w+)$/.test("123something") // ✅ /^(\d{3})(\w+)$/.test("1234") // ✅

Повторяющиеся символы, которые находятся после закрывающей группу скобки, относятся ко всей группе:

/^(\d{2})+$/ /^(\d{2})+$/.test("12") // ✅ /^(\d{2})+$/.test("123") // ❌ /^(\d{2})+$/.test("1234") // ✅

Захват групп

До сих пор мы видели, как тестировать строки и проверять, содержат ли они определенный шаблон.

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

Вы можете делать это с помощью групп, а точнее с помощью захвата групп .

По умолчанию, группы итак захватываются. Теперь вместо использования RegExp.test(String) , который просто возвращает логическое значение, мы будем использовать один из следующих методов:

  • String.match(RegExp)
  • RegExp.exec(String)

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

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

"123s".match(/^(\d{3})(\w+)$/) //Array [ "123s", "123", "123s" ] /^(\d{3})(\w+)$/.exec("123s") //Array [ "123s", "123", "s" ] "hey".match(/(hey|ho)/) //Array [ "hey", "hey" ] /(hey|ho)/.exec("hey") //Array [ "hey", "hey" ] /(hey|ho)/.exec("ha!") //null

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

"123456789".match(/(\d)+/) //Array [ "123456789", "9" ]

Опциональные группы

Захват групп можно сделать опциональным с помощью (...)? . Если ничего не будет найдено, то в возвращаемый массив будет добавлен элемент undefined:

/^(\d{3})(\s)?(\w+)$/.exec("123 s") //Array [ "123 s", "123", " ", "s" ] /^(\d{3})(\s)?(\w+)$/.exec("123s") //Array [ "123s", "123", undefined, "s" ]

Ссылка на найденную группу

Каждой найденной группе присваивается число. $1 ссылается на первый элемент, $2 на второй, и так далее. Это полезно, когда мы будет говорить о замене части строки.

Именованный захват групп

Это новая возможность ES2018.

Группе можно назначить имя, а не просто слот в возвращаемом массиве:

Const re = /(?\d{4})-(?\d{2})-(?\d{2})/ const result = re.exec("2015-01-02") // result.groups.year === "2015"; // result.groups.month === "01"; // result.groups.day === "02";

Использование match и exec без групп

Существует разница при использовании match и exec без групп: в первом элементе массива будет находится не полностью найденная строка, а прямое совпадение:

/hey|ho/.exec("hey") // [ "hey" ] /(hey).(ho)/.exec("hey ho") // [ "hey ho", "hey", "ho" ]

Незахватываемые группы

Так как по умолчанию группы являются захватываемыми, нам нужен способ игнорировать некоторые группы в возвращаемом массиве. Это возможно с помощью незахватываемых групп , которые начинаются с (?:...) .

"123s".match(/^(\d{3})(?:\s)(\w+)$/) // null "123 s".match(/^(\d{3})(?:\s)(\w+)$/) // Array [ "123 s", "123", "s" ]

Флаги

Вы можете использовать следующие флаги на любых регулярных выражениях:

  • g: ищет совпадения глобально
  • i: делает регулярное выражение не чувствительным к регистру
  • m: включает многострочный режим. В этом режиме ^ и $ совпадают с началом и концом всей строки. Без этого флага, с многострочными строками они совпадают с началом и концом каждой строки.
  • u: включает поддержку Unicode (добавлено в ES6/ES2015)
  • s: (новое в ES2018) сокращение от "single line", он позволяет. совпадать с символами новой строки

Флаги можно комбинировать, а также они добавляются в конец строки литерала:

/hey/ig.test("HEy") // ✅

или передаются вторым параметром в конструктор объекта RegExp:

New RegExp("hey", "ig").test("HEy") // ✅

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

Вы можете инспектировать свойства регулярных выражений:

  • source - строка шаблона
  • multiline - принимается значение true если установлен флаг m
  • global - принимается значение true если установлен флаг g
  • ignoreCase - принимается значение true если установлен флаг i
  • lastIndex
/^(\w{3})$/i.source //"^(\\d{3})(\\w+)$" /^(\w{3})$/i.multiline //false /^(\w{3})$/i.lastIndex //0 /^(\w{3})$/i.ignoreCase //true /^(\w{3})$/i.global //false Экранирование

Специальные символы:

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

/^\\$/ /^\^$/ // /^\^$/.test("^") ✅ /^\$$/ // /^\$$/.test("$") ✅ Границы строк

\b и \B позволяют определить находится ли строка в начале или конце слова:

  • \b совпадает если набор символов находится в начале или конце слова
  • \B совпадает если набор символов не находится в начале или конце слова

"I saw a bear".match(/\bbear/) //Array ["bear"] "I saw a beard".match(/\bbear/) //Array ["bear"] "I saw a beard".match(/\bbear\b/) //null "cool_bear".match(/\bbear\b/) //null

Замена с помощью регулярных выражений

Мы уже видели как нужно проверять строки на совпадение с шаблоном.

Также мы видели как можно извлекать часть строк соотвествующие шаблону в массив.

Теперь давайте рассмотрим как заменять части строки на основе шаблона.

У объекта String в JavaScript есть метод replace() , который можно использовать без регулярных выражений для одной замены в строке:

"Hello world!".replace("world", "dog") //Hello dog! "My dog is a good dog!".replace("dog", "cat") //My cat is a good dog!

Этот метод также может принимать и регулярное выражение в качестве аргумента:

"Hello world!".replace(/world/, "dog") //Hello dog!

Использование флага g - это единственный способ заменить несколько вхождений в строке на ванильном JavaScript:

"My dog is a good dog!".replace(/dog/g, "cat") //My cat is a good cat!

Группы позволяют нам делать больше причудливых вещей, менять местами части строк:

"Hello, world!".replace(/(\w+), (\w+)!/, "$2: $1!!!") // "world: Hello!!!"

Вместо строки можно использовать функцию, чтобы делать ещё более интересные вещи. В неё будет передан ряд аргументов, таких как возвращают методы String.match(RegExp) или RegExp.exec(String) , где количество аргументов зависит от количества групп:

"Hello, world!".replace(/(\w+), (\w+)!/, (matchedString, first, second) => { console.log(first); console.log(second); return `${second.toUpperCase()}: ${first}!!!` }) //"WORLD: Hello!!!"

Жадность

Регулярные выражения называются жадными по умолчанию.

Что это значит?

Возьмём например это регулярное выражение:

/\$(.+)\s?/

Предполагается, что нам нужно извлечь из строки сумму в долларах:

/\$(.+)\s?/.exec("This costs $100") //0

но что если у нас есть больше слов после числа, это отвлекает

/\$(.+)\s?/.exec("This costs $100 and it is less than $200") //100 and it is less than $200

Почему? Потому что регулярное выражение после знака $ совпадает с любым символом.+ и не останавливается пока не достигнет конца строки. Затем он останавливается, потому что \s? делает конечное пространство необязательным.

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

/\$(.+?)\s/.exec("This costs $100 and it is less than $200") //100

Итак, символ? может означать разные вещи в зависимости от своего положения, поэтому он может быть и квантификатором и индикатором ленивого режима.

Опережение: соответствие строки в зависимости от того что за ней следует

Используйет?= для поиска совпадений в строке за которой следует определённая подстрока

/Roger(?=Waters)/ /Roger(?= Waters)/.test("Roger is my dog") //false /Roger(?= Waters)/.test("Roger is my dog and Roger Waters is a famous musician") //true

Выполняет обратную операцию и находит совпадений в строке за которыми не следует определённая подстрока:

/Roger(?!Waters)/ /Roger(?! Waters)/.test("Roger is my dog") //true /Roger(?! Waters)/.test("Roger is my dog and Roger Waters is a famous musician") //false

Ретроспектива: соответствие строки в зависимости от того что ей предшествует

Это новая возможность ES2018.

Опережение использует символ?= . Ретроспектива использует?

Флаг u является обязательным при работе с Unicode строками, в частности когда может понадобится обрабатывать строки в астральных плоскостях, которые не включены в первые 1600 символов Unicode.

Например эмодзи, но и только они.

/^.$/.test("a") // ✅ /^.$/.test("?") // ❌ /^.$/u.test("?") // ✅

Поэтому, всегда используйте флаг u .

Unicode, как и обычные символы, может обрабатывать диапазоны:

//.test("a") // ✅ //.test("1") // ✅ /[?-?]/u.test("?") // ✅ /[?-?]/u.test("?") // ❌

JavaScript проверяет внутренние коды представления, поэтому? < ? < ? на самом деле \u1F436 < \u1F43A < \u1F98A . Посмотрите полный список эмодзи чтобы увидеть коды и узнать их порядок.

Экранирование свойств Unicode

Как мы говорили выше, в шаблоне регулярного выражения вы можете использовать \d чтобы найти совпадение на любую цифру, \s чтобы найти совпадение на любой символ кроме пробела, \w чтобы найти совпадение на любой буквенно-числовой символ и т. д.

Экранирование свойств Unicode - это возможность ES2018, которая добавляет очень крутую функцию, расширяя эту концепцию на всех Unicode символы и добавляя \p{} и \P{} .

У любого Unicode символа есть набор свойств. Например Script определяет семейство языков, ASCII - это логическое значение равное true для ASCII символов и т.д. Вы можете положить это свойство в фигурные скобки и регулярное выражение будет проверять чтобы его значение было истинным:

/^\p{ASCII}+$/u.test("abc") // ✅ /^\p{ASCII}+$/u.test("ABC@") // ✅ /^\p{ASCII}+$/u.test("ABC?") // ❌

ASCII_Hex_Digit - это ещё одно логическое свойство, которое проверяет содержит ли строка тольк валидные шестнадцатеричные цифры:

/^\p{ASCII_Hex_Digit}+$/u.test("0123456789ABCDEF") //✅ /^\p{ASCII_Hex_Digit}+$/u.test("h")

Существует много других логических свойств, которые вы можете проверить просто добавив их имя в фигурные скобки, включая Uppercase , Lowercase , White_Space , Alphabetic , Emoji и другие:

/^\p{Lowercase}$/u.test("h") // ✅ /^\p{Uppercase}$/u.test("H") // ✅ /^\p{Emoji}+$/u.test("H") // ❌ /^\p{Emoji}+$/u.test("??") // ✅

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

/^\p{Script=Greek}+$/u.test("ελληνικά") // ✅ /^\p{Script=Latin}+$/u.test("hey") // ✅

Примеры Извлечение числа из строки

Предположим, что есть строка содержащая только одно число, которое нужно извлечь. /\d+/ должен сделать это:

"Test 123123329".match(/\d+/) // Array [ "123123329" ]

Поиск E-mail адреса:

Простейший подход заключается в проверке безпробельных символов до и после знака @ , с помощью \S:

/(\S+)@(\S+)\.(\S+)/ /(\S+)@(\S+)\.(\S+)/.exec("[email protected]") //["[email protected]", "copesc", "gmail", "com"]

Однако, это упрощенный пример, так как под него попадает множество не валидных E-mail адресов.

Захват текста между двойными кавычками

Представим, что у вас есть строка, которая содержит текст заключённый в двойные кавычки и вам нужно извлечь этот текст.

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

Мы найдём то что нам нужно в result :

Const hello = "Hello "nice flower"" const result = /"([^"]*)"/.exec(hello) //Array [ "\"nice flower\"", "nice flower" ]

Получение содержимого из HTML тега

Например получить содержимое из тега span , допуская при этом любое количество аргументов у тега:

/]*>(.*?)/ /]*>(.*?)/.exec("test") // null /]*>(.*?)/.exec("test") // ["test", "test"] /]*>(.*?)/.exec("test") // ["test", "test"]

Регулярные выражения

Регулярное выражение - это объект, описывающий символьный шаблон. Класс RegExp в JavaScript представляет регулярные выражения, а объекты классов String и RegExp определяют методы, использующие регулярные выражения для выполнения поиска по шаблону и операций поиска в тексте с заменой. Грамматика регулярных выражений в языке JavaScript содержит достаточно полное подмножество синтаксиса регулярных выражений, используемого в языке Perl 5, поэтому, если вы имеете опыт работы с языком Perl, то вы без труда сможете описывать шаблоны в программах на языке JavaScript.

В число особенностей регулярных выражений языка Perl, которые не поддерживаются в ECMAScript, входят флаги s (однострочный режим) и x (расширенный синтаксис); управляющие последовательности \a, \e, \l, \u, \L, \U, \E, \Q, \A, \Z, \z и \G и другие расширенные конструкции, начинающиеся с (?.

Определение регулярных выражений

В JavaScript регулярные выражения представлены объектами RegExp . Объекты RegExp могут быть созданы посредством конструктора RegExp(), но чаще они создаются с помощью специального синтаксиса литералов. Так же как строковые литералы задаются в виде символов, заключенных в кавычки, литералы регулярных выражений задаются в виде символов, заключенных в пару символов слэша (/). Таким образом, JavaScript-код может содержать строки, похожие на эту:

Var pattern = /s$/;

Эта строка создает новый объект RegExp и присваивает его переменной pattern. Данный объект RegExp ищет любые строки, заканчивающиеся символом "s". Это же регулярное выражение может быть определено с помощью конструктора RegExp():

Var pattern = new RegExp("s$");

Спецификация шаблона регулярного выражения состоит из последовательности символов. Большая часть символов, включая все алфавитно-цифровые, буквально описывают символы, которые должны присутствовать. То есть регулярное выражение /java/ совпадает со всеми строками, содержащими подстроку «java».

Другие символы в регулярных выражениях не предназначены для поиска их точных эквивалентов, а имеют особое значение. Например, регулярное выражение /s$/ содержит два символа. Первый символ s обозначает поиск буквального символа. Второй, $ - это специальный метасимвол, обозначающий конец строки. Таким образом, это регулярное выражение соответствует любой строке, заканчивающейся символом s.

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

Символы литералов

Как отмечалось ранее, все алфавитные символы и цифры в регулярных выражениях соответствуют сами себе. Синтаксис регулярных выражений в JavaScript также поддерживает возможность указывать некоторые неалфавитные символы с помощью управляющих последовательностей, начинающихся с символа обратного слэша (\). Например, последовательность \n соответствует символу перевода строки. Эти символы перечислены в таблице ниже:

Некоторые знаки препинания имеют в регулярных выражениях особый смысл:

^ $ . * + ? = ! : | \ / () { } -

Значение этих символов раскрывается в последующих разделах. Некоторые из них имеют специальный смысл только в определенных контекстах регулярных выражений, а в других контекстах трактуются буквально. Однако, как правило, чтобы включить какой-либо из этих символов в регулярное выражение буквально, необходимо поместить перед ним символ обратного слэша. Другие символы, такие как кавычки и @, не имеют специального значения и просто соответствуют в регулярных выражениях самим себе.

Если вы не можете точно вспомнить, каким из символов должен предшествовать символ \, можете спокойно помещать обратный слэш перед любым из символов. Однако имейте в виду, что многие буквы и цифры вместе с символом слэша обретают специальное значение, поэтому тем буквам и цифрам, которые вы ищете буквально, не должен предшествовать символ \. Чтобы включить в регулярное выражение сам символ обратного слэша, перед ним, очевидно, следует поместить другой символ обратного слэша. Например, следующее регулярное выражение соответствует любой строке, содержащей символ обратного слэша: /\\/.

Классы символов

Отдельные символы литералов могут объединяться в классы символов путем помещения их в квадратные скобки. Класс символов соответствует любому символу, содержащемуся в этом классе. Следовательно, регулярное выражение // соответствует одному из символов a, b или c.

Могут также определяться классы символов с отрицанием, соответствующие любому символу, кроме тех, которые указаны в скобках. Класс символов с отрицанием задается символом ^ в качестве первого символа, следующего за левой скобкой. Регулярное выражение /[^abc]/ соответствует любому символу, отличному от a, b или c. В классах символов диапазон символов может задаваться при помощи дефиса. Поиск всех символов латинского алфавита в нижнем регистре осуществляется посредством выражения //, а любую букву или цифру из набора символов Latin можно найти при помощи выражения //.

Некоторые классы символов используются особенно часто, поэтому синтаксис регулярных выражений в JavaScript включает специальные символы и управляющие (escape) последовательности для их обозначения. Так, \s соответствует символам пробела, табуляции и любым пробельным символам из набора Unicode, а \S - любым символам, не являющимся пробельными символами из набора Unicode.

В таблице ниже приводится перечень этих спецсимволов и синтаксиса классов символов. (Обратите внимание, что некоторые из управляющих последовательностей классов символов соответствуют только ASCII-символам и не расширены для работы с Unicode-символами. Можно явно определить собственные классы Unicode-символов, например, выражение /[\u0400-\u04FF]/ соответствует любому символу кириллицы.)

Классы символов регулярных выражений JavaScript Символ Соответствие
[...] Любой из символов, указанных в скобках
[^...] Любой из символов, не указанных в скобках
. Любой символ, кроме перевода строки или другого разделителя Unicode-строки
\w Любой текстовый ASCII-символ. Эквивалентно
\W Любой символ, не являющийся текстовым ASCII-символом. Эквивалентно [^a-zA-Z0-9_]
\s Любой пробельный символ из набора Unicode
\S Любой непробельный символ из набора Unicode. Обратите внимание, что символы \w и \S - это не одно и то же
\d Любые ASCII-цифры. Эквивалентно
\D Любой символ, отличный от ASCII-цифр. Эквивалентно [^0-9]
[\b] Литерал символа «забой»

Обратите внимание, что управляющие последовательности специальных символов классов могут находиться в квадратных скобках. \s соответствует любому пробельному символу, а \d соответствует любой цифре, следовательно, /[\s\d]/ соответствует любому пробельному символу или цифре.

Повторение

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

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

Следующие строки демонстрируют несколько примеров:

Var pattern = /\d{2,4}/; // Соответствует числу, содержащему от двух до четырех цифр pattern = /\w{3}\d?/; // Соответствует в точности трем символам слова и одной необязательной цифре pattern = /\s+java\s+/; // Соответствует слову "java" с одним или более пробелами // до и после него pattern = /[^(]*/; // Соответствует нулю или более символам, отличным от открывающей круглой скобки

Будьте внимательны при использовании символов повторения * и?. Они могут соответствовать отсутствию указанного перед ними шаблона и, следовательно, отсутствию символов. Например, регулярному выражению /a*/ соответствует строка "bbbb", поскольку в ней нет символа a.

Символы повторения, перечисленные в таблице, соответствуют максимально возможному количеству повторений, при котором обеспечивается поиск последующих частей регулярного выражения. Мы говорим, что это - «жадное» повторение. Имеется также возможность реализовать повторение, выполняемое «нежадным» способом. Достаточно указать после символа (или символов) повторения вопросительный знак: ??, +?, *? или даже {1,5}?.

Например, регулярное выражение /a+/ соответствует одному или более экземплярам буквы a. Примененное к строке «aaa», оно соответствует всем трем буквам. С другой стороны, выражение /a+?/ соответствует одному или более экземплярам буквы a и выбирает наименее возможное число символов. Примененный к той же строке, этот шаблон соответствует только первой букве a.

«Нежадное» повторение не всегда дает ожидаемый результат. Рассмотрим шаблон /a+b/, соответствующий одному или более символам a, за которыми следует символ b. Применительно к строке «aaab» ему соответствует вся строка.

Теперь проверим «нежадную» версию /a+?b/. Можно было бы подумать, что она должна соответствовать символу b, перед которым стоит только один символ a. В случае применения к той же строке «aaab» можно было бы ожидать, что она совпадет с единственным символом a и последним символом b. Однако на самом деле этому шаблону соответствует вся строка, как и в случае «жадной» версии. Дело в том, что поиск по шаблону регулярного выражения выполняется путем нахождения первой позиции в строке, начиная с которой соответствие становится возможным. Так как соответствие возможно, начиная с первого символа строки, более короткие соответствия, начинающиеся с последующих символов, даже не рассматриваются.

Альтернативы, группировка и ссылки

Грамматика регулярных выражений включает специальные символы определения альтернатив, подвыражений группировки и ссылок на предыдущие подвыражения. Символ вертикальной черты | служит для разделения альтернатив. Например, /ab|cd|ef/ соответствует либо строке «ab», либо строке «cd», либо строке «ef», а шаблон /\d{3}|{4}/ - либо трем цифрам, либо четырем строчным буквам.

Обратите внимание, что альтернативы обрабатываются слева направо до тех пор, пока не будет найдено соответствие. При обнаружении совпадения с левой альтернативой правая игнорируется, даже если можно добиться «лучшего» соответствия. Поэтому, когда к строке «ab» применяется шаблон /a|ab/, он будет соответствовать только первому символу.

Круглые скобки имеют в регулярных выражениях несколько значений. Одно из них - группировка отдельных элементов в одно подвыражение, так что элементы при использовании специальных символов |, *, +, ? и других рассматриваются как одно целое. Например, шаблон /java(script)?/ соответствует слову «java», за которым следует необязательное слово «script», а /(ab|cd)+|ef)/ соответствует либо строке «ef», либо одному или более повторений одной из строк «ab» или «cd».

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

Предположим, что требуется отыскать одну или более букв в нижнем регистре, за которыми следует одна или несколько цифр. Для этого можно воспользоваться шаблоном /+\d+/. Но предположим также, что нам нужны только цифры в конце каждого соответствия. Если поместить эту часть шаблона в круглые скобки (/+(\d+)/), то можно будет извлечь цифры из любых найденных нами соответствий. Как это делается, будет описано ниже.

С этим связано еще одно применение подвыражений в скобках, позволяющее ссылаться на подвыражения из предыдущей части того же регулярного выражения. Это достигается путем указания одной или нескольких цифр после символа \. Цифры ссылаются на позицию подвыражения в скобках внутри регулярного выражения. Например, \1 ссылается на первое подвыражение, а \3 - на третье. Обратите внимание, что подвыражения могут быть вложены одно в другое, поэтому при подсчете используется позиция левой скобки. Например, в следующем регулярном выражении ссылка на вложенное подвыражение (cript) будет выглядеть как \2:

/(ava(cript)?)\sis\s(fun\w*)/

Ссылка на предыдущее подвыражение указывает не на шаблон этого подвыражения, а на найденный текст, соответствующий этому шаблону. Поэтому ссылки могут использоваться для наложения ограничения, выбирающего части строки, содержащие точно такие же символы. Например, следующее регулярное выражение соответствует нулю или более символам внутри одинарных или двойных кавычек. Однако оно не требует, чтобы открывающие и закрывающие кавычки соответствовали друг другу (т.е. чтобы обе кавычки были одинарными или двойными):

/[""][^""]*[""]/

Соответствия кавычек мы можем потребовать посредством такой ссылки:

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

Возможна также группировка элементов в регулярном выражении без создания нумерованной ссылки на эти элементы. Вместо простой группировки элементов между (и) начните группу с символов (?: и закончите ее символом). Рассмотрим, например, следующий шаблон:

/(ava(?:cript)?)\sis\s(fun\w*)/

Здесь подвыражение (?:cript) необходимо только для группировки, чтобы к группе мог быть применен символ повторения?. Эти модифицированные скобки не создают ссылку, поэтому в данном регулярном выражении \2 ссылается на текст, соответствующий шаблону (fun\w*).

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

Символы регулярных выражений выбора из альтернатив, группировки и ссылки JavaScript Символ Значение
| Альтернатива. Соответствует либо подвыражению слева, либо подвыражению справа.
(...) Группировка. Группирует элементы в единое целое, которое может использоваться с символами *, +, ?, | и т.п. Также запоминает символы, соответствующие этой группе для использования в последующих ссылках.
(?:...) Только группировка. Группирует элементы в единое целое, но не запоминает символы, соответствующие этой группе.
\number Соответствует тем же символам, которые были найдены при сопоставлении с группой с номером number. Группы - это подвыражения внутри скобок (возможно, вложенных). Номера группам присваиваются путем подсчета левых скобок слева направо. Группы, сформированные с помощью символов (?:, не нумеруются.
Указание позиции соответствия

Как описывалось ранее, многие элементы регулярного выражения соответствуют одному символу в строке. Например, \s соответствует одному пробельному символу. Другие элементы регулярных выражений соответствуют позициям между символами, а не самим символам. Например, \b соответствует границе слова - границе между \w (текстовый ASCII-символ) и \W (нетекстовый символ) или границе между текстовым ASCII-символом и началом или концом строки.

Такие элементы, как \b, не определяют какие-либо символы, которые должны присутствовать в найденной строке, однако они определяют допустимые позиции для проверки соответствия. Иногда эти элементы называются якорными элементами регулярных выражений, потому что они закрепляют шаблон за определенной позицией в строке. Чаще других используются такие якорные элементы, как ^ и $, привязывающие шаблоны соответственно к началу и концу строки.

Например, слово «JavaScript», находящееся на отдельной строке, можно найти с помощью регулярного выражения /^JavaScript$/. Чтобы найти отдельное слово «Java» (а не префикс, например в слове «JavaScript»), можно попробовать применить шаблон /\sJava\s/, который требует наличия пробела до и после слова.

Но такое решение порождает две проблемы. Во-первых, оно найдет слово «Java», только если оно окружено пробелами с обеих сторон, и не сможет найти его в начале или в конце строки. Во-вторых, когда этот шаблон действительно найдет соответствие, возвращаемая им строка будет содержать ведущие и замыкающие пробелы, а это не совсем то, что нам нужно. Поэтому вместо шаблона, совпадающего с пробельными символами \s, мы воспользуемся шаблоном (или якорем), совпадающим с границами слова \b. Получится следующее выражение: /\bJava\b/.

Якорный элемент \B соответствует позиции, не являющейся границей слова. То есть шаблону /\Bcript/ будут соответствовать слова «JavaScript» и «postscript» и не будут соответствовать слова «script» или «Scripting».

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

Например, чтобы найти совпадение с названием распространенного языка программирования, за которым следует двоеточие, можно воспользоваться выражением /ava(cript)?(?=\:)/. Этому шаблону соответствует слово «JavaScript» в строке «JavaScript: The Definitive Guide», но ему не будет соответствовать слово «Java» в строке «Java in a Nutshell», потому что за ним не следует двоеточие.

Если же ввести условие (?!, то это будет негативная опережающая проверка на последующие символы, требующая, чтобы следующие символы не соответствовали указанному шаблону. Например, шаблону /Java(?!Script)(\w*)/ соответствует подстрока «Java», за которой следует заглавная буква и любое количество текстовых ASCII-символов при условии, что за подстрокой «Java» не следует подстрока «Script». Он совпадет со строкой «JavaBeans», но не совпадет со строкой «Javanese», совпадет со строкой «JavaScrip», но не совпадет со строками «JavaScript» или «JavaScripter».

В таблице ниже приводится перечень якорных символов регулярных выражений:

Якорные символы регулярных выражений Символ Значение
^ Соответствует началу строкового выражения или началу строки при многострочном поиске.
$ Соответствует концу строкового выражения или концу строки при многострочном поиске.
\b Соответствует границе слова, т.е. соответствует позиции между символом \w и символом \W или между символом \w и началом или концом строки. (Однако обратите внимание, что [\b] соответствует символу забоя.)
\B Соответствует позиции, не являющейся границей слов.
(?=p) Позитивная опережающая проверка на последующие символы. Требует, чтобы последующие символы соответствовали шаблону p, но не включает эти символы в найденную строку.
(?!p) Негативная опережающая проверка на последующие символы. Требует, чтобы следующие символы не соответствовали шаблону p.
Флаги

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

Флаг i указывает, что поиск по шаблону должен быть нечувствителен к регистру символов, а флаг g - что поиск должен быть глобальным, т.е. должны быть найдены все соответствия в строке. Флаг m выполняет поиск по шаблону в многострочном режиме. Если строковое выражение, в котором выполняется поиск, содержит символы перевода строк, то в этом режиме якорные символы ^ и $, помимо того, что они соответствуют началу и концу всего строкового выражения, также соответствуют началу и концу каждой текстовой строки. Например, шаблону /java$/im соответствует как слово «java», так и «Java\nis fun».

Эти флаги могут объединяться в любые комбинации. Например, чтобы выполнить поиск первого вхождения слова «java» (или «Java», «JAVA» и т.д.) без учета регистра символов, можно воспользоваться нечувствительным к регистру регулярным выражением /\bjava\b/i. А чтобы найти все вхождения этого слова в строке, можно добавить флаг g: /\bjava\b/gi.

Методы класса String для поиска по шаблону

До этого момента мы обсуждали грамматику создаваемых регулярных выражений, но не рассматривали, как эти регулярные выражения могут фактически использоваться в JavaScript-сценариях. В данном разделе мы обсудим методы объекта String, в которых регулярные выражения применяются для поиска по шаблону, а также для поиска с заменой. А затем продолжим разговор о поиске по шаблону с регулярными выражениями, рассмотрев объект RegExp, его методы и свойства.

Строки поддерживают четыре метода, использующие регулярные выражения. Простейший из них - метод search() . Он принимает в качестве аргумента регулярное выражение и возвращает либо позицию первого символа найденной подстроки, либо -1, если соответствие не найдено. Например, следующий вызов вернет 4:

Var result = "JavaScript".search(/script/i); // 4

Если аргумент метода search() не является регулярным выражением, он сначала преобразуется путем передачи конструктору RegExp. Метод search() не поддерживает глобальный поиск и игнорирует флаг g в своем аргументе.

Метод replace() выполняет операцию поиска с заменой. Он принимает в качестве первого аргумента регулярное выражение, а в качестве второго - строку замены. Метод отыскивает в строке, для которой он вызван, соответствие указанному шаблону.

Если регулярное выражение содержит флаг g, метод replace() заменяет все найденные совпадения строкой замены. В противном случае он заменяет только первое найденное совпадение. Если первый аргумент метода replace() является строкой, а не регулярным выражением, то метод выполняет буквальный поиск строки, а не преобразует его в регулярное выражение с помощью конструктора RegExp(), как это делает метод search().

В качестве примера мы можем воспользоваться методом replace() для единообразной расстановки прописных букв в слове «JavaScript» для всей строки текста:

// Независимо от регистра символов заменяем словом в нужном регистре var result = "javascript".replace(/JavaScript/ig, "JavaScript");

Метод replace() представляет собой более мощное средство, чем можно было бы предположить по этому примеру. Напомню, что подвыражения в скобках, находящиеся внутри регулярного выражения, нумеруются слева направо, и что регулярное выражение запоминает текст, соответствующий каждому из подвыражений. Если в строке замены присутствует знак $ с цифрой, метод replace() заменяет эти два символа текстом, соответствующим указанному подвыражению. Это очень полезная возможность. Мы можем использовать ее, например, для замены прямых кавычек в строке типографскими кавычками, которые имитируются ASCII-символами:

// Цитата - это кавычка, за которой следует любое число символов, // отличных от кавычек (их мы запоминаем), за этими символами // следует еще одна кавычка var quote = /"([^"]*)"/g; // Заменяем прямые кавычки типографскими и оставляем без изменений «$1» // содержимое цитаты, хранящееся в $1 var text = ""JavaScript" - интерпретируемый язык программирования."; var result = text.replace(quote, "«$1»"); // «JavaScript» - интерпретируемый язык программирования.

Важное, что следует отметить, - второй аргумент replace() может быть функцией, динамически вычисляющей строку замены.

Метод match() - это наиболее общий из методов класса String, использующих регулярные выражения. Он принимает в качестве единственного аргумента регулярное выражение (или преобразует свой аргумент в регулярное выражение, передав его конструктору RegExp()) и возвращает массив, содержащий результаты поиска. Если в регулярном выражении установлен флаг g, метод возвращает массив всех соответствий, присутствующих в строке. Например:

// вернет ["1", "2", "3"] var result = "1 плюс 2 равно 3".match(/\d+/g);

Если регулярное выражение не содержит флаг g, метод match() не выполняет глобальный поиск; он просто ищет первое совпадение. Однако match() возвращает массив, даже когда метод не выполняет глобальный поиск. В этом случае первый элемент массива - это найденная подстрока, а все оставшиеся элементы представляют собой подвыражения регулярного выражения. Поэтому если match() возвращает массив arr, то arr будет содержать найденную строку целиком, arr -подстроку, соответствующую первому подвыражению, и т.д. Проводя параллель с методом replace(), можно сказать, что в arr[n] заносится содержимое $n.

Например, взгляните на следующий программный код, выполняющий разбор URL-адреса:

Var url = /(\w+):\/\/([\w.]+)\/(\S*)/; var text = "Посетите наш сайт http://www..php"; var result = text.match(url); if (result != null) { var fullurl = result; // Содержит "http://www..php" var protocol = result; // Содержит "http" var host = result; // Содержит "www..php" }

Следует отметить, что для регулярного выражения, в котором не установлен флаг g глобального поиска, метод match() возвращает то же значение, что и метод exec() регулярного выражения: возвращаемый массив имеет свойства index и input, как описывается в обсуждении метода exec() ниже.

Последний из методов объекта String, в котором используются регулярные выражения - split() . Этот метод разбивает строку, для которой он вызван, на массив подстрок, используя аргумент в качестве разделителя. Например:

"123,456,789".split(","); // Вернет ["123","456","789"]

Метод split() может также принимать в качестве аргумента регулярное выражение. Это делает метод более мощным. Например, можно указать разделитель, допускающий произвольное число пробельных символов с обеих сторон:

"1, 2, 3 , 4 , 5".split(/\s*,\s*/); // Вернет ["1","2","3","4","5"]

Объект RegExp

Как было упомянуто, регулярные выражения представлены в виде объектов RegExp. Помимо конструктора RegExp(), объекты RegExp поддерживают три метода и несколько свойств.

Конструктор RegExp() принимает один или два строковых аргумента и создает новый объект RegExp. Первый аргумент конструктора - это строка, содержащая тело регулярного выражения, т.е. текст, который должен находиться между символами слэша в литерале регулярного выражения. Обратите внимание, что в строковых литералах и регулярных выражениях для обозначения управляющих последовательностей используется символ \, поэтому, передавая конструктору RegExp() регулярное выражение в виде строкового литерала, необходимо заменить каждый символ \ парой символов \\.

Второй аргумент RegExp() может отсутствовать. Если он указан, то определяет флаги регулярного выражения. Это должен быть один из символов g, i, m либо комбинация этих символов. Например:

// Находит все пятизначные числа в строке. Обратите внимание // на использование в этом примере символов \\ var zipcode = new RegExp("\\d{5}", "g");

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

Свойства RegExp

Каждый объект RegExp имеет пять свойств. Свойство source - строка, доступная только для чтения, содержащая текст регулярного выражения. Свойство global - логическое значение, доступное только для чтения, определяющее наличие флага g в регулярном выражении. Свойство ignoreCase - это логическое значение, доступное только для чтения, определяющее наличие флага i в регулярном выражении. Свойство multiline - это логическое значение, доступное только для чтения, определяющее наличие флага m в регулярном выражении. И последнее свойство lastIndex - это целое число, доступное для чтения и записи. Для шаблонов с флагом g это свойство содержит номер позиции в строке, с которой должен быть начат следующий поиск. Как описано ниже, оно используется методами exec() и test().

Методы RegExp

Объекты RegExp определяют два метода, выполняющие поиск по шаблону; они ведут себя аналогично методам класса String, описанным выше. Основной метод класса RegExp, используемый для поиска по шаблону - exec() . Он похож на упоминавшийся метод match() класса String, за исключением того, что является методом класса RegExp, принимающим в качестве аргумента строку, а не методом класса String, принимающим аргумент RegExp.

Метод exec() выполняет регулярное выражение для указанной строки, т.е. ищет совпадение в строке. Если совпадение не найдено, метод возвращает null. Однако если соответствие найдено, он возвращает такой же массив, как массив, возвращаемый методом match() для поиска без флага g. Нулевой элемент массива содержит строку, соответствующую регулярному выражению, а все последующие элементы - подстроки, соответствующие всем подвыражениям. Кроме того, свойство index содержит номер позиции символа, которым начинается соответствующий фрагмент, а свойство input ссылается на строку, в которой выполнялся поиск.

В отличие от match(), метод exec() возвращает массив, структура которого не зависит от наличия в регулярном выражении флага g. Напомню, что при передаче глобального регулярного выражения метод match() возвращает массив найденных соответствий. А exec() всегда возвращает одно соответствие, но предоставляет о нем полную информацию. Когда exec() вызывается для регулярного выражения, содержащего флаг g, метод устанавливает свойство lastIndex объекта регулярного выражения равным номеру позиции символа, следующего непосредственно за найденной подстрокой.

Когда метод exec() вызывается для того же регулярного выражения второй раз, он начинает поиск с символа, позиция которого указана в свойстве lastIndex. Если exec() не находит соответствия, свойство lastIndex получает значение 0. (Вы также можете установить lastIndex в ноль в любой момент, что следует делать во всех тех случаях, когда поиск завершается до того, как будет найдено последнее соответствие в одной строке, и начинается поиск в другой строке с тем же объектом RegExp.) Это особое поведение позволяет вызывать exec() повторно для перебора всех соответствий регулярному выражению в строке. Например:

Var pattern = /Java/g; var text = "JavaScript - это более забавная штука, чем Java!"; var result; while((result = pattern.exec(text)) != null) { console.log("Найдено "" + result + """ + " в позиции " + result.index + "; следующий поиск начнется с " + pattern.lastIndex); }

Еще один метод объекта RegExp - test() , который намного проще метода exec(). Он принимает строку и возвращает true, если строка соответствует регулярному выражению:

Var pattern = /java/i; pattern.test("JavaScript"); // Вернет true

Вызов test() эквивалентен вызову exec(), возвращающему true, если exec() возвращает не null. По этой причине метод test() ведет себя так же, как метод exec() при вызове для глобального регулярного выражения: он начинает искать указанную строку с позиции, заданной свойством lastIndex, и если находит соответствие, устанавливает свойство lastIndex равным номеру позиции символа, непосредственно следующего за найденным соответствием. Поэтому с помощью метода test() можно так же сформировать цикл обхода строки, как с помощью метода exec().

В JavaScript регулярные выражения представлены объектами RegExp . Объекты RegExp могут быть созданы посредством конструктора RegExp(), но чаще они создаются с помощью специального синтаксиса литералов. Так же как строковые литералы задаются в виде символов, заключенных в кавычки, литералы регулярных выражений задаются в виде символов, заключенных в пару символов слэша / .

/pattern/флаги new RegExp("pattern"[, опции поиска])

pattern - регулярное выражение для поиска (о замене - позже), а флаги - строка из любой комбинации символов g(глобальный поиск), i(регистр неважен) и m(многострочный поиск). Первый способ используется часто, второй - иногда. Например, два таких вызова эквивалентны.

Опции поиска

При создании регулярного выражения мы можем указать дополнительных опции поиска

Символы в регулярных выражениях JavaScriptСимвол Соответствие
Алфавитно-цифровые символы Соответствуют сами себе
\0 Символ NUL (\u0000)
\t Табуляция (\u0009)
\n Перевод строки (\u000A)
\v Вертикальная табуляция (\u000B)
\f Перевод страницы (\u000C)
\r Возврат каретки (\u000D)
\xnn Символ из набора Latin, задаваемый шестнадцатеричным числом nn; например, \x0A - это то же самое, что \n
\uxxxx Unicode-символ, заданный шестнадцатеричным числом xxxx; например, \u0009 - это то же самое, что \t
\cX Управляющий символ "X", например, последовательность \cJ эквивалентна символу перевода строки \n
\ Для обычных символов - делает их специальными. Например, выражение /s/ ищет просто символ "s". А если поставить \ перед s, то /\s/ уже обозначает пробельный символ.И наоборот, если символ специальный, например *, то \ сделает его просто обычным символом "звездочка". Например, /a*/ ищет 0 или больше подряд идущих символов "a". Чтобы найти а со звездочкой "a*" - поставим \ перед спец. символом: /a\*/ .
^ Обозначает начало входных данных. Если установлен флаг многострочного поиска ("m") , то также сработает при начале новой строки.Например, /^A/ не найдет "A" в "an A", но найдет первое "A" в "An A."
$ Обозначает конец входных данных. Если установлен флаг многострочного поиска, то также сработает в конце строки.Например, /t$/ не найдет "t" в "eater", но найдет - в "eat".
* Обозначает повторение 0 или более раз. Например, /bo*/ найдет "boooo" в "A ghost booooed" и "b" в "A bird warbled", но ничего не найдет в "A goat grunted".
+ Обозначает повторение 1 или более раз. Эквивалентно {1,} . Например, /a+/ найдет "a" в "candy" и все "a" в "caaaaaaandy".
? Обозначает, что элемент может как присутствовать, так и отсутствовать. Например, /e?le?/ найдет "el" в "angel" и "le" в "angle."Если используется сразу после одного из квантификаторов * , + , ? , или {} , то задает "нежадный" поиск (повторение минимально возможное количество раз, до ближайшего следующего элемента паттерна), в противоположность "жадному" режиму по умолчанию, при котором количество повторений максимально, даже если следующий элемент паттерна тоже подходит.Кроме того, ? используется в предпросмотре, который описан в таблице под (?=) , (?!) , и (?:) .
. (Десятичная точка) обозначает любой символ, кроме перевода строки: \n \r \u2028 or \u2029. (можно использовать [\s\S] для поиска любого символа, включая переводы строк). Например, /.n/ найдет "an" и "on" в "nay, an apple is on the tree", но не "nay".
(x) Находит x и запоминает. Это называется "запоминающие скобки". Например, /(foo)/ найдет и запомнит "foo" в "foo bar." Найденная подстрока хранится в массиве-результате поиска или в предопределенных свойствах объекта RegExp: $1, ..., $9 .Кроме того, скобки объединяют то, что в них находится, в единый элемент паттерна. Например, (abc)* - повторение abc 0 и более раз.
(?:x) Находит x , но не запоминает найденное. Это называется "незапоминающие скобки". Найденная подстрока не сохраняется в массиве результатов и свойствах RegExp.Как и все скобки, объединяют находящееся в них в единый подпаттерн.
x(?=y) Находит x , только если за x следует y . Например, /Jack(?=Sprat)/ найдет "Jack", только если за ним следует "Sprat". /Jack(?=Sprat|Frost)/ найдет "Jack", только если за ним следует "Sprat" или "Frost". Однако, ни "Sprat" ни "Frost" не войдут в результат поиска.
x(?!y) Находит x , только если за x не следует y . Например, /\d+(?!\.)/ найдет число, только если за ним не следует десятичная точка. /\d+(?!\.)/.exec("3.141") найдет 141, но не 3.141.
x|y Находит x или y . Например, /green|red/ найдет "green" в "green apple" и "red" в "red apple."
{n} Где n - положительное целое число. Находит ровно n повторений предшествующего элемента. Например, /a{2}/ не найдет "a" в "candy," но найдет оба a в "caandy," и первые два a в "caaandy."
{n,} Где n - положительное целое число. Находит n и более повторений элемента. Например, /a{2,} не найдет "a" в "candy", но найдет все "a" в "caandy" и в "caaaaaaandy."
{n,m} Где n и m - положительные целые числа. Находят от n до m повторений элемента.
Набор символов. Находит любой из перечисленных символов. Вы можете указать промежуток, используя тире. Например, - то же самое, что . Найдет "b" в "brisket", а также "a" и "c" в "ache".
[^xyz] Любой символ, кроме указанных в наборе. Вы также можете указать промежуток. Например, [^abc] - то же самое, что [^a-c] . Найдет "r" в "brisket" и "h" в "chop."
[\b] Находит символ backspace. (Не путать с \b .)
\b Находит границу слов (латинских), например пробел. (Не путать с [\b]). Например, /\bn\w/ найдет "no" в "noonday"; /\wy\b/ найдет "ly" в "possibly yesterday."
\B Обозначает не границу слов. Например, /\w\Bn/ найдет "on" в "noonday", а /y\B\w/ найдет "ye" в "possibly yesterday."
\cX Где X - буква от A до Z. Обозначает контрольный символ в строке. Например, /\cM/ обозначает символ Ctrl-M.
\d находит цифру из любого алфавита (у нас же юникод). Испльзуйте , чтобы найти только обычные цифры. Например, /\d/ или // найдет "2" в "B2 is the suite number."
\D Найдет нецифровой символ (все алфавиты). [^0-9] - эквивалент для обычных цифр. Например, /\D/ или /[^0-9]/ найдет "B" в "B2 is the suite number."
\s Найдет любой пробельный символ, включая пробел, табуляцию, переводы строки и другие юникодные пробельные символы. Например, /\s\w*/ найдет " bar" в "foo bar."
\S Найдет любой символ, кроме пробельного. Например, /\S\w*/ найдет "foo" в "foo bar."
\v Символ вертикальной табуляции.
\w Найдет любой словесный (латинский алфавит) символ, включая буквы, цифры и знак подчеркивания. Эквивалентно . Например, /\w/ найдет "a" в "apple," "5" в "$5.28," и "3" в "3D."
\W Найдет любой не-(лат.)словесный символ. Эквивалентно [^A-Za-z0-9_] . Например, /\W/ и /[^$A-Za-z0-9_]/ одинаково найдут "%" в "50%."
Работа с регулярными выражениями в Javascript

Работа с регулярными выражениями в Javascript реализована методами класса String

exec(regexp) - находит все совпадения (вхождения в шаблон "регулярки") в строке. Возвращает массив (при совпадении) и обновляет свойство regexp-а, или null - если ничего не найдено,. С модификатором g - при каждом вызове этой функции, она будет возвращать следующее совпадение после предыдущего найденного - это реализовано с помощью ведения индекса смещения последнего поиска.

match(regexp) - найти часть строки по шаблону. Если указан модификатор g, то функция match() возвращает массив всех совпадений или null (а не пустой массив). Без модификатора g эта функция работает как exec();

test(regexp) - функция проверяет строку на соответствие шаблону. Возвращает true - если есть совпадение, и false - если совпадения нет.

split(regexp) - разбивает строку, для которой он вызван, на массив подстрок, используя аргумент в качестве разделителя.

replace(regexp, mix) - метод возвращает строку изменную в соответствии с шаблоном (регуляррным выражением). Первый параметр regexp также может содержать строку, а не регулярное выражение. Без модификатора g - метод в строке заменяет только первое вхождение; с модификатором g - происходит глобальная замена, т.е. меняются все вхождения в данной строке. mix - шаблон замены, может принитать значения строки, шаблона замены, функции (имя функции).

Спецсимволы в строке заменыЗамена через функцию

Если Вы указываете вторым параметром функцию, то она выполняется при каждом совпадении. В функции можно динамически генерировать и возвращать строку подстановки. Первый параметр функции - найденная подстрока. Если первым аргументом replace является объект RegExp, то следующие n параметров содержат совпадения из вложенных скобок. Последние два параметра - позиция в строке, на которой произошло совпадение и сама строка.

Синтаксис регулярных выражений является достаточно сложным и его изучение требует серьёзных усилий. Наилучшим руководством по регулярным выражением на сегодняшний день является книга Дж. Фридла "Регулярные выражения", позволяющая, по словам автора, "научиться мыслить регулярными выражениями".

Основные понятия

Регулярное выражение (regular expression) - средство для обработки строк или последовательность символов, определяющая шаблон текста.

Модификатор - предназначен для "инструктирования" регулярного выражения.

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

Регулярное выражение задаётся как обычная переменная, только вместо кавычек используется слэш, например: var reg=/рег_выражение/

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

Допустим, нашей задачей является замена всех букв "р" (малых и заглавных) на латинскую большую букву "R" в словосочетании Регулярные выражения .

Создаём шаблон var reg=/р/ и воспользуясь методом replace осуществляем задуманное



var reg=/р/

document.write(result)

В результате получим строку - РегуляRные выражения , замена произошла только на первом вхождении буквы "р" с учётом регистра.

Но под условия нашей задачи этот результат не подходит... Тут нам понадобятся модификаторы "g" и "i", которые могут использоваться как отдельно, так и совместно. Эти модификаторы ставятся в конце шаблона регулярного выражения, после слэша, и имеют следующие значения:

модификатор "g" - задаёт поиск в строке как "глобальный", т.е. в нашем случае замена произойдет для всех вхождений буквы "р". Теперь шаблон выглядит так: var reg=/р/g , подставив его в наш код


var str="Регулярные выражения"
var reg=/р/g
var result=str.replace(reg, "R")
document.write(result)

получим строку - РегуляRные выRажения .

модификатор "i" - задаёт поиск в строке без учёта регистра, добавив этот модификатор в наш шаблон var reg=/р/gi , после выполнения скрипта получим искомый результат нашей задачи - RегуляRные выRажения .

Специальные символы (метасимволы)

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

  • Метасимволы поиска совпадений.
  • Количественные метасимволы.
  • Метасимволы позиционирования.
Метасимволы поиска совпадений

Значение

Описание

граница слова

задаёт условие, при котором шаблон должен выполняться в начале или конце слова

/\ber/ совпадает с error , не совпадает с hero или с player
/er/ совпдает с player , не совпадает с hero или с error
/\ber\b/ не совпадает с hero или с player или с error , может совпасть только с er

не граница слова

задаёт условие, при котором шаблон не выполняется в начале или конце слова

/\Ber/ совпадает с hero или с player , не совпадает с error
/er\B/ совпадает с error или с player , не совпадает с hero
/\Ber\B/ совпадает с hero , не совпадает с player или с error

цифра от 0 до 9

/\d\d\d\d/ совпадает с любым четырёх значным числом

/\D\D\D\D/ не совпадёт с 2005 или 05.г или №126 и т.д.

одиночный пустой символ

соответствует символу пробела

\over\sbyte\ совпадает только с over byte

одиночный непустой символ

любой один символ за исключением пробела

\over\Sbyte\ совпадает с over-byte или с over_byte , не совпадает с over byte или over--byte

буква, цифра или символ подчёркивания

/A\w/ совпадает с A1 или с AB , не совпадает с A+

не буква, цифра или символ подчёркивания

/A\W/ не совпадает с A1 или с AB , совпадает с A+

любой символ

любые знаки, буквы, цифры и т.д.

/.../ совпадает с любыми тремя символами ABC или !@4 или 1 q

набор символов

задаёт условие, при котором шаблон должен выполняться при любом совпадении символов заключенных в квадратные скобки

/WERTY/ совпадает с QWERTY , с AWERTY

набор не входящих символов

задаёт условие, при котором шаблон не должен выполняться при любом совпадении символов заключенных в квадратные скобки

/[^QA]WERTY/ не совпадает с QWERTY , с AWERTY

Символы, приведенные в таблице "Метасимволы поиска совпадений" не надо путать с последовательностью знаков перехода используемых в строках, таких как \\t - табуляция, \\n - переход на новую строку и т.д.

Количественные метасимволы

Количество совпадений

Ноль и большее количество раз

/Ja*vaScript/ совпадает с JvaScript или с JaavaScript или с JaaavaScript , не совпадает с JovaScript

Ноль или один раз

/Ja?vaScript/ совпадает только с JvaScript или с JavaScript

Один и большее количество раз

/Ja+vaScript/ совпадает с JavaScript или с JaavaScript или с JaaavaScript , не совпадает с JvaScript

точно n раз

/Ja{2}vaScript/ совпадает только с JaavaScript

n или большее количество раз

/Ja{2,}vaScript/ совпадает с JaavaScript или с JaaavaScript , не совпадает с JvaScript или с JavaScript

по крайней мере, n раз, но не более чем m раз

/Ja{2,3}vaScript/ совпадает только с JaavaScript или с JaaavaScript

Каждый символ, приведенный в таблице "Количественные метасимволы" применяется к одному предшествующему символу или метасимволу в регулярном выражении.

Метасимволы позиционирования

Последний набор метасимволов предназначен для обозначения, где искать (если это важно) подстроку в начале строки или в конце.

Некоторые методы для работы с шаблонами

replace - данный метод мы уже использовали в самом начале статьи, он предназначен для поиска образца и замены найденной подстроки на новую подстроку.

exec - данный метод выполняет сопоставление строки с образцом, заданным шаблоном. Если сопоставление с образцом закончилось неудачей, то возвращается значение null . В противном случае результатом является массив подстрок, соответствующих заданному образцу. /*Первый элемент массива будет равен исходной строке удовлетворяющее заданному шаблону*/

например:


var reg=/(\d+).(\d+).(\d+)/
var arr=reg.exec("Я родился 15.09.1980")
document.write("Дата рождения: ", arr, "< br>")
document.write("День рождения: ", arr, "< br>")
document.write("Месяц рождения: ", arr, "< br>")
document.write("Год рождения: ", arr, "< br>")

в результате получим четыре строки:
Дата рождения: 15.09.1980
День рождения: 15
Месяц рождения: 09
Год рождения: 1980

Заключение

В статье отображено далеко не все возможности и прелести регулярных выражений, для более глубокого изучения этого вопроса посоветую изучить объект RegExp. Так же хочу обратить внимание на то, что синтаксис регулярных выражений не чем не отличается как в JavaScript, так и в PHP. К примеру, для проверки правильности ввода e-mail, регулярное выражение, что для JavaScript, что для PHP будет выглядеть одинаково /+@+.{2,3}/i .

Некоторые люди, столкнувшись с проблемой, думают: «О, а использую-ка я регулярные выражения». Теперь у них есть две проблемы.
Джейми Завински

Юан-Ма сказал: «Требуется большая сила, чтобы резать дерево поперёк структуры древесины. Требуется много кода, чтобы программировать поперёк структуры проблемы.
Мастер Юан-Ма, «Книга программирования»

Инструменты и техники программирования выживают и распространяются хаотично-эволюционным способом. Иногда выживают не красивые и гениальные, а просто такие, которые достаточно хорошо работают в своей области – к примеру, если их интегрируют в другую успешную технологию.

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

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

Создаём регулярное выражение Регулярка – тип объекта. Её можно создать, вызвав конструктор RegExp, или написав нужный шаблон, окружённый слешами.

Var re1 = new RegExp("abc"); var re2 = /abc/;

Оба этих регулярных выражения представляют один шаблон: символ “a”, за которым следует символ “b”, за которым следует символ “c”.

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

Вторая запись, где шаблон находится между слешами, обрабатывает обратные слеши по-другому. Во-первых, так как шаблон заканчивается прямым слешем, то нужно ставить обратный слеш перед прямым слешем, который мы хотим включить в наш шаблон. Кроме того, обратные слеши, не являющиеся частью специальных символов типа \n, будут сохранены (а не проигнорированы, как в строках), и изменят смысл шаблона. У некоторых символов, таких, как знак вопроса или плюс, есть особое значение в регулярках, и если вам нужно найти такой символ, его также надо предварять обратным слешем.

Var eighteenPlus = /eighteen\+/;

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

Проверяем на совпадения У регулярок есть несколько методов. Простейший – test. Если передать ему строку, он вернёт булевское значение, сообщая, содержит ли строка вхождение заданного шаблона.

Console.log(/abc/.test("abcde")); // → true console.log(/abc/.test("abxde")); // → false

Регулярка, состоящая только из неспециальных символов, просто представляет собой последовательность этих символов. Если abc есть где-то в строке, которую мы проверяем (не только в начале), test вернёт true.

Ищем набор символов Выяснить, содержит ли строка abc, можно было бы и при помощи indexOf. Регулярки позволяют пройти дальше и составлять более сложные шаблоны.

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

Оба выражения находятся в строчках, содержащих цифру.

Console.log(//.test("in 1992")); // → true console.log(//.test("in 1992")); // → true

В квадратных скобках тире между двумя символами используется для задания диапазона символов, где последовательность задаётся кодировкой Unicode. Символы от 0 до 9 находятся там просто подряд (коды с 48 до 57), поэтому захватывает их все и совпадает с любой цифрой.

У нескольких групп символов есть свои встроенные сокращения.

\d Любая цифра
\w Алфавитно-цифровой символ
\s Пробельный символ (пробел, табуляция, перевод строки, и т.п.)
\D не цифра
\W не алфавитно-цифровой символ
\S не пробельный символ
. любой символ, кроме перевода строки

Таким образом можно задать формат даты и времени вроде 30-01-2003 15:20 следующим выражением:

Var dateTime = /\d\d-\d\d-\d\d\d\d \d\d:\d\d/; console.log(dateTime.test("30-01-2003 15:20")); // → true console.log(dateTime.test("30-jan-2003 15:20")); // → false

Выглядит ужасно, не так ли? Слишком много обратных слешей, которые затрудняют понимание шаблона. Позже мы слегка улучшим его.

Обратные слеши можно использовать и в квадратных скобках. Например, [\d.] означает любую цифру или точку. Заметьте, что точка внутри квадратных скобок теряет своё особое значение и превращается просто в точку. То же касается и других специальных символов, типа +.

Инвертировать набор символов – то есть, сказать, что вам надо найти любой символ, кроме тех, что есть в наборе – можно, поставив знак ^ сразу после открывающей квадратной скобки.

Var notBinary = /[^01]/; console.log(notBinary.test("1100100010100110")); // → false console.log(notBinary.test("1100100010200110")); // → true

Повторяем части шаблона Мы знаем, как найти одну цифру. А если нам надо найти число целиком – последовательность из одной или более цифр?

Если поставить после чего-либо в регулярке знак +, это будет означать, что этот элемент может быть повторён более одного раза. /\d+/ означает одну или несколько цифр.

Console.log(/"\d+"/.test(""123"")); // → true console.log(/"\d+"/.test("""")); // → false console.log(/"\d*"/.test(""123"")); // → true console.log(/"\d*"/.test("""")); // → true

У звёздочки * значение почти такое же, но она разрешает шаблону присутствовать ноль раз. Если после чего-то стоит звёздочка, то оно никогда не препятствует нахождению шаблона в строке – оно просто находится там ноль раз.

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

Var neighbor = /neighbou?r/; console.log(neighbor.test("neighbour")); // → true console.log(neighbor.test("neighbor")); // → true

Чтобы задать точное количество раз, которое шаблон должен встретиться, используются фигурные скобки. {4} после элемента означает, что он должен встретиться в строке 4 раза. Также можно задать промежуток: {2,4} означает, что элемент должен встретиться не менее 2 и не более 4 раз.

Ещё одна версия формата даты и времени, где разрешены дни, месяцы и часы из одной или двух цифр. И ещё она чуть более читаема.

Var dateTime = /\d{1,2}-\d{1,2}-\d{4} \d{1,2}:\d{2}/; console.log(dateTime.test("30-1-2003 8:45")); // → true

Можно использовать промежутки с открытым концом, опуская одно из чисел. {,5} означает, что шаблон может встретиться от нуля до пяти раз, а {5,} – от пяти и более.

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

Var cartoonCrying = /boo+(hoo+)+/i; console.log(cartoonCrying.test("Boohoooohoohooo")); // → true

Первый и второй плюсы относятся только ко вторым буквам о в словах boo и hoo. Третий + относится к целой группе (hoo+), находя одну или несколько таких последовательностей.

Буква i в конце выражения делает регулярку нечувствительной к регистру симолов – так, что B совпадает с b.

Совпадения и группы Метод test – самый простой метод проверки регулярок. Он только сообщает, было ли найдено совпадение, или нет. У регулярок есть ещё метод exec, который вернёт null, если ничего не было найдено, а в противном случае вернёт объект с информацией о совпадении.

Var match = /\d+/.exec("one two 100"); console.log(match); // → ["100"] console.log(match.index); // → 8

У возвращаемого exec объекта есть свойство index, где содержится номер символа, с которого случилось совпадение. А вообще объект выглядит как массив строк, где первый элемент – строка, которую проверяли на совпадение. В нашем примере это будет последовательность цифр, которую мы искали.

У строк есть метод match, работающий примерно так же.

Console.log("one two 100".match(/\d+/)); // → ["100"]

Когда в регулярке содержатся подвыражения, сгруппированные круглыми скобками, текст, совпавший с этими группами, тоже появится в массиве. Первый элемент всегда совпадение целиком. Второй – часть, совпавшая с первой группой (той, у кого круглые скобки встретились раньше всех), затем со второй группой, и так далее.

Var quotedText = /"([^"]*)"/; console.log(quotedText.exec("she said "hello"")); // → [""hello"", "hello"]

Когда группа не найдена вообще (например, если за ней стоит знак вопроса), её позиция в массиве содержит undefined. Если группа совпала несколько раз, то в массиве будет только последнее совпадение.

Console.log(/bad(ly)?/.exec("bad")); // → ["bad", undefined] console.log(/(\d)+/.exec("123")); // → ["123", "3"]

Группы полезны для извлечения частей строк. Если нам не просто надо проверить, есть ли в строке дата, а извлечь её и создать представляющий дату объект, мы можем заключить последовательности цифр в круглые скобки и выбрать дату из результата exec.

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

Тип даты В JavaScript есть стандартный тип объекта для дат – а точнее, моментов во времени. Он называется Date. Если просто создать объект даты через new, вы получите текущие дату и ремя.

Console.log(new Date()); // → Sun Nov 09 2014 00:07:57 GMT+0300 (CET)

Также можно создать объект, содержащий заданное время

Console.log(new Date(2015, 9, 21)); // → Wed Oct 21 2015 00:00:00 GMT+0300 (CET) console.log(new Date(2009, 11, 9, 12, 59, 59, 999)); // → Wed Dec 09 2009 12:59:59 GMT+0300 (CET)

JavaScript использует соглашение, в котором номера месяцев начинаются с нуля, а номера дней – с единицы. Это глупо и нелепо. Поберегитесь.

Последние четыре аргумента (часы, минуты, секунды и миллисекунды) необязательны, и в случае отсутствия приравниваются к нулю.

Метки времени хранятся как количество миллисекунд, прошедших с начала 1970 года. Для времени до 1970 года используются отрицательные числа (это связано с соглашением по Unix time, которое было создано примерно в то время). Метод getTime объекта даты возвращает это число. Оно, естественно, большое.
console.log(new Date(2013, 11, 19).getTime()); // → 1387407600000 console.log(new Date(1387407600000)); // → Thu Dec 19 2013 00:00:00 GMT+0100 (CET)

Если задать конструктору Date один аргумент, он воспринимается как это количество миллисекунд. Можно получить текущее значение миллисекунд, создав объект Date и вызвав метод getTime, или же вызвав функцию Date.now.

У объекта Date для извлечения его компонентов есть методы getFullYear, getMonth, getDate, getHours, getMinutes, и getSeconds. Есть также метод getYear, возвращающий довольно бесполезный двузначный код, типа 93 или 14.

Заключив нужные части шаблона в круглые скобки, мы можем создать объект даты прямо из строки.

Function findDate(string) { var dateTime = /(\d{1,2})-(\d{1,2})-(\d{4})/; var match = dateTime.exec(string); return new Date(Number(match), Number(match) - 1, Number(match)); } console.log(findDate("30-1-2003")); // → Thu Jan 30 2003 00:00:00 GMT+0100 (CET)

Границы слова и строки К сожалению, findDate так же радостно извлечёт бессмысленную дату 00-1-3000 из строки «100-1-30000». Совпадение может случиться в любом месте строки, так что в данном случае он просто начнёт со второго символа и закончит на предпоследнем.

Если нам надо принудить совпадение взять всю строку целиком, мы используем метки ^ и $. ^ совпадает с началом строки, а $ с концом. Поэтому /^\d+$/ совпадает со строкой, состоящей только из одной или нескольких цифр, /^!/ совпадает со сторокой, начинающейся с восклицательного знака, а /x^/ не совпадает ни с какой строчкой (перед началом строки не может быть x).

Если, с другой стороны, нам просто надо убедиться, что дата начинается и заканчивается на границе слова, мы используем метку \b. Границей слова может быть начало или конец строки, или любое место строки, где с одной стороны стоит алфавитно-цифровой символ \w, а с другой – не алфавитно-цифровой.

Console.log(/cat/.test("concatenate")); // → true console.log(/\bcat\b/.test("concatenate")); // → false

Отметим, что метка границы не представляет из себя символ. Это просто ограничение, обозначающее, что совпадение происходит только если выполняется определённое условие.

Шаблоны с выбором Допустим, надо выяснить, содержит ли текст не просто номер, а номер, за которым следует pig, cow, или chicken в единственном или множественном числе.

Можно было бы написать три регулярки и проверить их по очереди, но есть способ лучше. Символ | обозначает выбор между шаблонами слева и справа от него. И можно сказать следующее:

Var animalCount = /\b\d+ (pig|cow|chicken)s?\b/; console.log(animalCount.test("15 pigs")); // → true console.log(animalCount.test("15 pigchickens")); // → false

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

Механизм поиска Регулярные выражения можно рассматривать как блок-схемы. Следующая диаграмма описывает последний животноводческий пример.

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

Значит, проверка совпадения нашей регулярки в строке «the 3 pigs» при прохождении по блок-схеме выглядит так:

На позиции 4 есть граница слова, и проходим первый прямоугольник
- начиная с 4 позиции находим цифру, и проходим второй прямоугольник
- на позиции 5 один путь замыкается назад перед вторым прямоугольником, а второй проходит далее к прямоугольнику с пробелом. У нас пробел, а не цифра, и мы выбираем второй путь.
- теперь мы на позиции 6, начало “pigs”, и на тройном разветвлении путей. В строке нет “cow” или “chicken”, зато есть “pig”, поэтому мы выбираем этот путь.
- на позиции 9 после тройного разветвления, один путь обходит “s” и направляется к последнему прямоугольнику с границей слова, а второй проходит через “s”. У нас есть “s”, поэтому мы идём туда.
- на позиции 10 мы в конце строки, и совпасть может только граница слова. Конец строки считается границей, и мы проходим через последний прямоугольник. И вот мы успешно нашли наш шаблон.

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

Откаты Регулярка /\b(+b|\d+|[\da-f]h)\b/ совпадает либо с двоичным числом, за которым следует b, либо с десятичным числом без суффикса, либо шестнадцатеричным (цифры от 0 до 9 или символы от a до h), за которым идёт h. Соответствующая диаграмма:

В поисках совпадения может случиться, что алгоритм пошёл по верхнему пути (двоичное число), даже если в строке нет такого числа. Если там есть строка “103”, к примеру, понятно, что только достигнув цифры 3 алгоритм поймёт, что он на неправильном пути. Вообще строка совпадает с регуляркой, просто не в этой ветке.

Тогда алгоритм совершает откат. На развилке он запоминает текущее положение (в нашем случае, это начало строки, сразу после границы слова), чтобы можно было вернуться назад и попробовать другой путь, если выбранный не срабатывает. Для строки “103” после встречи с тройкой он вернётся и попытается пройти путь для десятичных чисел. Это сработает, поэтому совпадение будет найдено.

Алгоритм останавливается, как только найдёт полное совпадение. Это значит, что даже если несколько вариантов могут подойти, используется только один из них (в том порядке, в каком они появляются в регулярке).

Откаты случаются при использовании операторов повторения, таких, как + и *. Если вы ищете /^.*x/ в строке «abcxe», часть регулярки.* попробует поглотить всю строчку. Алгоритм затем сообразит, что ему нужен ещё и “x”. Так как никакого “x” после конца строки нет, алгоритм попробует поискать совпадение, откатившись на один символ. После abcx тоже нет x, тогда он снова откатывается, уже к подстроке abc. И после строчки он находит x и докладывает об успешном совпадении, на позициях с 0 по 4.

Можно написать регулярку, которая приведёт ко множественным откатам. Такая проблема возникает, когда шаблон может совпасть с входными данными множеством разных способов. Например, если мы ошибёмся при написании регулярки для двоичных чисел, мы можем случайно написать что-то вроде /(+)+b/.

Если алгоритм будет искать такой шаблон в длинной строке из нолей и единиц, не содержащей в конце “b”, он сначала пройдёт по внутренней петле, пока у него не кончатся цифры. Тогда он заметит, что в конце нет “b”, сделает откат на одну позицию, пройдёт по внешней петле, опять сдастся, попытается откатиться на ещё одну позицию по внутренней петле… И будет дальше искать таким образом, задействуя обе петли. То есть, количество работы с каждым символом строки будет удваиваться. Даже для нескольких десятков символов поиск совпадения займёт очень долгое время.

Метод replace У строк есть метод replace, который может заменять часть строки другой строкой.

Console.log("папа".replace("п", "м")); // → мапа

Первый аргумент может быть и регулярной, в каком случае заменяется первое вхождение регулярки в строке. Когда к регулярке добавляется опция “g” (global, всеобщий), заменяются все вхождения, а не только первое

Console.log("Borobudur".replace(//, "a")); // → Barobudur console.log("Borobudur".replace(//g, "a")); // → Barabadar

Имело бы смысл передавать опцию «заменить все» через отдельный аргумент, или через отдельный метод типа replaceAll. Но к сожалению, опция передаётся через саму регулярку.

Вся сила регулярок раскрывается, когда мы используем ссылки на найденные в строке группы, заданные в регулярке. Например, у нас есть строка, содержащая имена людей, одно имя на строчку, в формате «Фамилия, Имя». Если нам надо поменять их местами и убрать запятую, чтобы получилось «Имя Фамилия», мы пишем следующее:

Console.log("Hopper, Grace\nMcCarthy, John\nRitchie, Dennis" .replace(/([\w ]+), ([\w ]+)/g, "$2 $1")); // → Grace Hopper // John McCarthy // Dennis Ritchie

$1 и $2 в строчке на замену ссылаются на группы символов, заключённые в скобки. $1 заменяется текстом, который совпал с первой группой, $2 – со второй группой, и так далее, до $9. Всё совпадение целиком содержится в переменной $&.

Также можно в качестве второго аргумента передавать и функцию. Для каждой замены будет вызвана функция, аргументами которой будут найденные группы (и вся совпадающая часть строки целиком), а её результат будет вставлен в новую строку.

Простой пример:

Var s = "the cia and fbi"; console.log(s.replace(/\b(fbi|cia)\b/g, function(str) { return str.toUpperCase(); })); // → the CIA and FBI

А вот более интересный:

Var stock = "1 lemon, 2 cabbages, and 101 eggs"; function minusOne(match, amount, unit) { amount = Number(amount) - 1; if (amount == 1) // остался только один, удаляем "s" в конце unit = unit.slice(0, unit.length - 1); else if (amount == 0) amount = "no"; return amount + " " + unit; } console.log(stock.replace(/(\d+) (\w+)/g, minusOne)); // → no lemon, 1 cabbage, and 100 eggs

Код принимает строку, находит все вхождения чисел, за которыми идёт слово, и возвращает строчку, где каждое число уменьшено на единицу.

Группа (\d+) попадает в аргумент amount, а (\w+) – в unit. Функция преобразовывает amount в число – и это всегда срабатывает, потому что наш шаблон как раз \d+. И затем вносит изменения в слово, на случай если остался всего 1 предмет.

Жадность Несложно при помощи replace написать функцию, убирающую все комментарии из кода JavaScript. Вот первая попытка:

Function stripComments(code) { return code.replace(/\/\/.*|\/\*[^]*\*\//g, ""); } console.log(stripComments("1 + /* 2 */3")); // → 1 + 3 console.log(stripComments("x = 10;// ten!")); // → x = 10; console.log(stripComments("1 /* a */+/* b */ 1")); // → 1 1

Часть перед оператором «или» совпадает с двумя слешами, за которыми идут любое количество символов, кроме символов перевода строки. Часть, убирающая многострочные комментарии, более сложна. Мы используем [^], т.е. любой символ, не являющийся пустым, в качестве способа найти любой символ. Мы не можем использовать точку, потому что блочные комментарии продолжаются и на новой строке, а символ перевода строки не совпадает с точкой.

Но вывод предыдущего примера неправильный. Почему?

Часть [^]* сначала попытается захватить столько символов, сколько может. Если из-за этого следующая часть регулярки не найдёт себе совпадения, произойдёт откат на один символ и попробует снова. В примере, алгоритм пытается захватить всю строку, и затем откатывается. Откатившись на 4 символа назад, он найдёт в строчке */ - а это не то, чего мы добивались. Мы-то хотели захватить только один комментарий, а не пройти до конца строки и найти последний комментарий.

Из-за этого мы говорим, что операторы повторения (+, *, ?, and {}) жадные, то есть они сначала захватывают, сколько могут, а потом идут назад. Если вы поместите вопрос после такого оператора (+?, *?, ??, {}?), они превратятся в нежадных, и начнут находить самые маленькие из возможных вхождений.

И это то, что нам нужно. Заставив звёздочку находить совпадения в минимально возможном количестве символов строчки, мы поглощаем только один блок комментариев, и не более того.

Function stripComments(code) { return code.replace(/\/\/.*|\/\*[^]*?\*\//g, ""); } console.log(stripComments("1 /* a */+/* b */ 1")); // → 1 + 1

Множество ошибок возникает при использовании жадных операторов вместо нежадных. При использовании оператора повтора сначала всегда рассматривайте вариант нежадного оператора.

Динамическое создание объектов RegExp В некоторых случаях точный шаблон неизвестен во время написания кода. Например, вам надо будет искать имя пользователя в тексте, и заключать его в подчёркивания. Так как вы узнаете имя только после запуска программы, вы не можете использовать запись со слешами.

Но вы можете построить строку и использовать конструктор RegExp. Вот пример:

Var name = "гарри"; var text = "А у Гарри на лбу шрам."; var regexp = new RegExp("\\b(" + name + ")\\b", "gi"); console.log(text.replace(regexp, "_$1_")); // → А у _Гарри_ на лбу шрам.

При создании границ слова приходится использовать двойные слеши, потому что мы пишем их в нормальной строке, а не в регулярке с прямыми слешами. Второй аргумент для RegExp содержит опции для регулярок – в нашем случае “gi”, т.е. глобальный и регистро-независимый.

Но что, если имя будет «dea+hlrd» (если наш пользователь – кульхацкер)? В результате мы получим бессмысленную регулярку, которая не найдёт в строке совпадений.

Мы можем добавить обратных слешей перед любым символом, который нам не нравится. Мы не можем добавлять обратные слеши перед буквами, потому что \b или \n – это спецсимволы. Но добавлять слеши перед любыми не алфавитно-цифровыми символами можно без проблем.

Var name = "dea+hlrd"; var text = "Этот dea+hlrd всех достал."; var escaped = name.replace(/[^\w\s]/g, "\\$&"); var regexp = new RegExp("\\b(" + escaped + ")\\b", "gi"); console.log(text.replace(regexp, "_$1_")); // → Этот _dea+hlrd_ всех достал.

Метод search Метод indexOf нельзя использовать с регулярками. Зато есть метод search, который как раз ожидает регулярку. Как и indexOf, он возвращает индекс первого вхождения, или -1, если его не случилось.

Console.log(" word".search(/\S/)); // → 2 console.log(" ".search(/\S/)); // → -1

К сожалению, никак нельзя задать, чтобы метод искал совпадение, начиная с конкретного смещения (как это можно сделать с indexOf). Это было бы полезно.

Свойство lastIndex Метод exec тоже не даёт удобного способа начать поиск с заданной позиции в строке. Но неудобный способ даёт.

У объекта регулярок есть свойства. Одно из них – source, содержащее строку. Ещё одно – lastIndex, контролирующее, в некоторых условиях, где начнётся следующий поиск вхождений.

Эти условия включают необходимость присутствия глобальной опции g, и то, что поиск должен идти с применением метода exec. Более разумным решением было бы просто допустить дополнительный аргумент для передачи в exec, но разумность – не основополагающая черта в интерфейсе регулярок JavaScript.

Var pattern = /y/g; pattern.lastIndex = 3; var match = pattern.exec("xyzzy"); console.log(match.index); // → 4 console.log(pattern.lastIndex); // → 5

Если поиск был успешным, вызов exec обновляет свойство lastIndex, чтоб оно указывало на позицию после найденного вхождения. Если успеха не было, lastIndex устанавливается в ноль – как и lastIndex у только что созданного объекта.

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

Var digit = /\d/g; console.log(digit.exec("here it is: 1")); // → ["1"] console.log(digit.exec("and now: 1")); // → null

Ещё один интересный эффект опции g в том, что она меняет работу метода match. Когда он вызывается с этой опцией, вместо возврата массива, похожего на результат работы exec, он находит все вхождения шаблона в строке и возвращает массив из найденных подстрок.

Console.log("Банан".match(/ан/g)); // → ["ан", "ан"]

Так что поосторожнее с глобальными переменными-регулярками. В случаях, когда они необходимы – вызовы replace или места, где вы специально используете lastIndex – пожалуй и все случаи, в которых их следует применять.

Циклы по вхождениям

Типичная задача – пройти по всем вхождениям шаблона в строку так, чтобы иметь доступ к объекту match в теле цикла, используя lastIndex и exec.

Var input = "Строчка с 3 числами в ней... 42 и 88."; var number = /\b(\d+)\b/g; var match; while (match = number.exec(input)) console.log("Нашёл ", match, " на ", match.index); // → Нашёл 3 на 14 // Нашёл 42 на 33 // Нашёл 88 на 40

Используется тот факт, что значением присвоения является присваиваемое значение. Используя конструкцию match = re.exec(input) в качестве условия в цикле while, мы производим поиск в начале каждой итерации, сохраняем результат в переменной, и заканчиваем цикл, когда все совпадения найдены.

Разбор INI файлы

В заключение главы рассмотрим задачу с использованием регулярок. Представьте, что мы пишем программу, собирающую сведения о наших врагах через интернет в автоматическом режиме. (Всю программу писать не будем, только ту часть, которая читает файл с настройками. Извините.) Файл выглядит так:

Searchengine=http://www.google.com/search?q=$1 spitefulness=9.7 ; перед комментариями ставится точка с запятой; каждая секция относится к отдельному врагу fullname=Larry Doe type=бычара из детсада website=http://www.geocities.com/CapeCanaveral/11451 fullname=Gargamel type=злой волшебник outputdir=/home/marijn/enemies/gargamel

Точный формат файла (который довольно широко используется, и обычно называется INI), следующий:

Пустые строки и строки, начинающиеся с точки с запятой, игнорируются
- строки, заключённые в квадратные скобки, начинают новую секцию
- строки, содержащие алфавитно-цифровой идентификатор, за которым следует =, добавляют настройку в данной секции

Всё остальное – неверные данные.

Наша задача – преобразовать такую строку в массив объектов, каждый со свойством name и массивом настроек. Для каждой секции нужен один объект, и ещё один – для глобальных настроек сверху файла.

Так как файл надо разбирать построчно, неплохо начать с разбиения файла на строки. Для этого в главе 6 мы использовали string.split("\n"). Некоторые операционки используют для перевода строки не один символ \n, а два - \r\n. Так как метод split принимает регулярки в качестве аргумента, мы можем делить линии при помощи выражения /\r?\n/, разрешающего и одиночные \n и \r\n между строками.

Function parseINI(string) { // Начнём с объекта, содержащего настройки верхнего уровня var currentSection = {name: null, fields: }; var categories = ; string.split(/\r?\n/).forEach(function(line) { var match; if (/^\s*(;.*)?$/.test(line)) { return; } else if (match = line.match(/^\[(.*)\]$/)) { currentSection = {name: match, fields: }; categories.push(currentSection); } else if (match = line.match(/^(\w+)=(.*)$/)) { currentSection.fields.push({name: match, value: match}); } else { throw new Error("Строчка "" + line + "" содержит неверные данные."); } }); return categories; }

Код проходит все строки, обновляя объект текущей секции “current section”. Сначала он проверяет, можно ли игнорировать строчку, при помощи регулярки /^\s*(;.*)?$/. Соображаете, как это работает? Часть между скобок совпадает с комментариями, а? делает так, что регулярка совпадёт и со строчками, состоящими из одних пробелов.

Если строка – не комментарий, код проверяет, начинает ли она новую секцию. Если да, он создаёт новый объект для текущей секции, к которому добавляются последующие настройки.

Последняя осмысленная возможность – строка является обычной настройкой, и в этом случае она добавляется к текущему объекту.

Если ни один вариант не сработал, функция выдаёт ошибку.

Заметьте, как частое использование ^ и $ заботится о том, что выражение совпадает со всей строкой целиком, а не с частью. Если их не использовать, код в целом будет работать, но иногда будет выдавать странные результаты, и такую ошибку будет трудно отследить.

Конструкция if (match = string.match(...)) похожа на трюк, использующий присвоение как условие в цикле while. Часто вы не знаете, что вызов match будет успешным, поэтому вы можете получить доступ к результирующему объекту только внутри блока if, который это проверяет. Чтоб не разбивать красивую цепочку проверок if, мы присваиваем результат поиска переменной, и сразу используем это присвоение как проверку.

Международные символы Из-за изначально простой реализации языка, и последующей фиксации такой реализации «в граните», регулярки JavaScript тупят с символами, не встречающимися в английском языке. К примеру, символ «буквы» с точки зрения регулярок JavaScript, может быть одним из 26 букв английского алфавита, и почему-то ещё подчёркиванием. Буквы типа é или β, однозначно являющиеся буквами, не совпадают с \w (и совпадут с \W, то есть с не-буквой).

По странному стечению обстоятельств, исторически \s (пробел) совпадает со всеми символами, которые в Unicode считаются пробельными, включая такие штуки, как неразрывный пробел или монгольский разделитель гласных.

У некоторых реализаций регулярок в других языках есть особый синтаксис для поиска специальных категорий символов Unicode, типа «все прописные буквы», «все знаки препинания» или «управляющие символы». Есть планы по добавлению таких категорий и в JavaScript, но они, видимо, будут реализованы не скоро.

Итог

Регулярки – это объекты, представляющие шаблоны поиска в строках. Они используют свой синтаксис для выражения этих шаблонов.

/abc/ Последовательность символов
// Любой символ из списка
/[^abc]/ Любой символ, кроме символов из списка
// Любой символ из промежутка
/x+/ Одно или более вхождений шаблона x
/x+?/ Одно или более вхождений, нежадное
/x*/ Ноль или более вхождений
/x?/ Ноль или одно вхождение
/x{2,4}/ От двух до четырёх вхождений
/(abc)/ Группа
/a|b|c/ Любой из нескольких шаблонов
/\d/ Любая цифра
/\w/ Любой алфавитно-цифровой символ («буква»)
/\s/ Любой пробельный символ
/./ Любой символ, кроме переводов строки
/\b/ Граница слова
/^/ Начало строки
/$/ Конец строки

У регулярки есть метод test, для проверки того, есть ли шаблон в строке. Есть метод exec, возвращающий массив, содержащий все найденные группы. У массива есть свойство index, где содержится номер символа, с которого случилось совпадение.

У строк есть метод match для поиска шаблонов, и метод search, возвращающий только начальную позицию вхождения. Метод replace может заменять вхождения шаблона на другую строку. Кроме этого, вы можете передать в replace функцию, которая будет строить строчку на замену, основываясь на шаблоне и найденных группах.

У регулярок есть настройки, которые пишут после закрывающего слеша. Опция i делает регулярку регистронезависимой, а опция g делает её глобальной, что, кроме прочего, заставляет метод replace заменять все найденные вхождения, а не только первое.

Конструктор RegExp можно использовать для создания регулярок из строк.

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

Упражнения Неизбежно при решении задач у вас возникнут непонятные случаи, и вы можете иногда отчаиваться, видя непредсказуемое поведение некоторых регулярок. Иногда помогает изучить поведение регулярки через онлайн-сервис типа debuggex.com, где можно посмотреть её визуализацию и сравнить с желаемым эффектом.Регулярный гольф «Гольфом» в коде называют игру, где нужно выразить заданную программу минимальным количеством символов. Регулярный гольф – практическое упражнение по написанию наименьших возможных регулярок для поиска заданного шаблона, и только его.

Для каждой из подстрочек напишите регулярку для проверки их нахождения в строке. Регулярка должна находить только эти указанные подстроки. Не волнуйтесь насчёт границ слов, если это не упомянуто особо. Когда у вас получится работающая регулярка, попробуйте её уменьшить.

Car и cat
- pop и prop
- ferret, ferry, и ferrari
- Любое слово, заканчивающееся на ious
- Пробел, за которым идёт точка, запятая, двоеточие или точка с запятой.
- Слово длинее шести букв
- Слово без букв e

// Впишите свои регулярки verify(/.../, ["my car", "bad cats"], ["camper", "high art"]); verify(/.../, ["pop culture", "mad props"], ["plop"]); verify(/.../, ["ferret", "ferry", "ferrari"], ["ferrum", "transfer A"]); verify(/.../, ["how delicious", "spacious room"], ["ruinous", "consciousness"]); verify(/.../, ["bad punctuation ."], ["escape the dot"]); verify(/.../, ["hottentottententen"], ["no", "hotten totten tenten"]); verify(/.../, ["red platypus", "wobbling nest"], ["earth bed", "learning ape"]); function verify(regexp, yes, no) { // Ignore unfinished exercises if (regexp.source == "...") return; yes.forEach(function(s) { if (!regexp.test(s)) console.log("Не нашлось "" + s + """); }); no.forEach(function(s) { if (regexp.test(s)) console.log("Неожиданное вхождение "" + s + """); }); }

Кавычки в тексте Допустим, вы написали рассказ, и везде для обозначения диалогов использовали одинарные кавычки. Теперь вы хотите заменить кавычки диалогов на двойные, и оставить одинарные в сокращениях слов типа aren’t.

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

Снова числа Последовательности цифр можно найти простой регуляркой /\d+/.

Напишите выражение, находящее только числа, записанные в стиле JavaScript. Оно должно поддерживать возможный минус или плюс перед числом, десятичную точку, и экспоненциальную запись 5e-3 или 1E10 – опять-таки с возможными плюсом или минусом. Также заметьте, что до или после точки не обязательно могут стоять цифры, но при этом число не может состоять из одной точки. То есть, .5 или 5. – допустимые числа, а одна точка сама по себе – нет.

// Впишите сюда регулярку. var number = /^...$/; // Tests: ["1", "-1", "+15", "1.55", ".5", "5.", "1.3e2", "1E-4", "1e+12"].forEach(function(s) { if (!number.test(s)) console.log("Не нашла "" + s + """); }); ["1a", "+-1", "1.2.3", "1+1", "1e4.5", ".5.", "1f5", "."].forEach(function(s) { if (number.test(s)) console.log("Неправильно принято "" + s + """); });

Статьи по теме: