← JavaScript/Регулярные выражения#101 из 383← ПредыдущийСледующий →+30 XP
Полезно по теме:Гайд: как учить JavaScriptПрактика: JS базаПрактика: async и сетьТермин: Closure

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

На GitHub при создании PR система автоматически находит упоминания задач вида #1234, email-адреса, ссылки и превращает их в кликабельные элементы. В Notion поиск работает по всему тексту. В VS Code функция «найти и заменить» с регулярными выражениями позволяет за секунды переименовать паттерн во всём проекте. Всё это — регулярные выражения.

Какую проблему решает

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

На основе предыдущих уроков

  • Строки — методы строк: replace, match, includes
  • Типы данных — RegExp как тип данных
  • Создание RegExp

    const re1 = /hello/i                    // литеральный синтаксис — предпочтительный
    const re2 = new RegExp('hello', 'i')    // конструктор — нужен когда паттерн динамический
    
    // Динамический паттерн — только через конструктор
    const searchTerm = 'javascript'
    const re3 = new RegExp(searchTerm, 'gi')

    Флаги

  • g — global: найти все вхождения, не только первое
  • i — case-insensitive: игнорировать регистр
  • m — multiline: ^ и $ работают для каждой строки текста
  • Основные паттерны

    .          // любой символ кроме переноса строки
    \\d         // цифра [0-9]
    \\D         // не цифра
    \\w         // буква, цифра или _ [a-zA-Z0-9_]
    \\W         // не \\w
    \\s         // пробельный символ (пробел, таб, перенос)
    \\S         // не пробел
    
    +          // 1 или более
    *          // 0 или более
    ?          // 0 или 1 (необязательный)
    {n}        // ровно n
    {n,m}      // от n до m
    [abc]      // один из символов a, b, c
    [a-z]      // диапазон
    [^abc]     // не a, b, c
    ^          // начало строки
    $          // конец строки
    (a|b)      // a или b

    Методы

    /\\d+/.test('abc123')               // true — есть ли совпадение
    'abc123'.match(/\\d+/)             // ['123'] — первое совпадение
    'abc123def456'.match(/\\d+/g)      // ['123', '456'] — все совпадения (флаг g)
    'hello'.replace(/l/g, 'r')          // 'herro'
    'a1b2c3'.replace(/\\d/g, '_')     // 'a_b_c_'
    
    // matchAll — итерируемый список всех совпадений с подробностями
    const all = [...'a1b22c333'.matchAll(/\\d+/g)]
    all.map(m => m[0])  // ['1', '22', '333']

    Группы захвата

    // Именованные группы — предпочтительны в сложных паттернах
    const dateRe = /(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})/
    const m = '2024-06-20'.match(dateRe)
    m.groups  // { year: '2024', month: '06', day: '20' }
    
    // Обычные группы (нумерованные)
    const m2 = '2024-06-20'.match(/(\\d{4})-(\\d{2})-(\\d{2})/)
    m2[1]  // '2024'
    m2[2]  // '06'

    Замена с функцией-обработчиком

    // Шаблонизатор: {{name}} → реальное значение
    const template = 'Привет, {{name}}! Заказ {{orderId}} готов.'
    const vars = { name: 'Иван', orderId: '#1234' }
    
    const result = template.replace(/\{\{(\w+)\}\}/g, (_, key) => vars[key] ?? _)
    // 'Привет, Иван! Заказ #1234 готов.'

    Типичные ошибки

    Ошибка 1: забыли флаг g — match возвращает только первое вхождение

    const text = 'тел: +7-916-123, доп: +7-499-456'
    
    // Без g — только первый номер
    text.match(/\\+7[\\d-]+/)    // ['+7-916-123']
    
    // С g — все номера
    text.match(/\\+7[\\d-]+/g)  // ['+7-916-123', '+7-499-456']

    Ошибка 2: test() с флагом g меняет lastIndex

    const re = /\\d+/g  // флаг g сохраняет позицию между вызовами!
    
    re.test('abc123')   // true
    re.test('abc123')   // false — поиск начался с позиции 6 (после '123')
    re.test('abc123')   // true — позиция сбросилась
    
    // Безопасно: используй новый RegExp или /паттерн/.test() без g для test()
    /\\d+/.test('abc123')   // true — всегда работает

    Ошибка 3: не экранировали спецсимволы

    // Хотим найти точку буквально, но . в regex — любой символ
    '3.14'.match(/3.14/)   // ['3.14'] — совпадёт, но и '3x14' тоже!
    '3.14'.match(/3\\.14/) // ['3.14'] — только с точкой
    
    // Хотим найти скобку
    'fn(x)'.match(/fn\\(x\\)/)  // нужно экранировать

    В реальных проектах

  • Валидация форм: email, телефон, пароль, СНИЛС, ИНН
  • Шаблонизаторы: замена {{переменных}} в HTML-шаблонах
  • Парсинг: извлечение цен, дат, ссылок из текста
  • Форматирование: номер карты 4-4-4-4, телефон +7 (999) 123-45-67
  • Линтеры и Code Review: поиск запрещённых паттернов в коде
  • Примеры

    Валидация форм, извлечение данных и шаблонизатор — реальные задачи

    // 1. Валидация email и российского телефона
    const emailRe = /^[\w.-]+@[\w.-]+\.[a-z]{2,}$/i
    console.log(emailRe.test('user@example.com'))   // true
    console.log(emailRe.test('bad@.com'))           // false
    console.log(emailRe.test('no-at-sign'))         // false
    
    const phoneRe = /^\+7\d{10}$/
    console.log(phoneRe.test('+79161234567'))  // true
    console.log(phoneRe.test('89161234567'))   // false — нет +7
    
    // 2. Извлечение всех чисел из текста (корзина магазина)
    const receipt = 'Товары: 3 шт., итого 1500 руб., скидка 10%, к оплате 1350 руб.'
    const numbers = receipt.match(/\d+/g)
    console.log(numbers)  // ['3', '1500', '10', '1350']
    
    // 3. Именованные группы — парсинг даты из строки
    const dateRe = /(?<day>\d{2})\.(?<month>\d{2})\.(?<year>\d{4})/
    const match = '25.12.2024'.match(dateRe)
    const { day, month, year } = match.groups
    console.log(`${year}-${month}-${day}`)  // '2024-12-25'
    
    // 4. Шаблонизатор уведомлений (как в SendPulse или Unisender)
    const template = 'Здравствуйте, {{name}}! Ваш заказ {{orderId}} на сумму {{total}}₽ готов.'
    const vars = { name: 'Иван', orderId: '#A-1234', total: '2500' }
    
    const notification = template.replace(/\{\{(\w+)\}\}/g, (_, key) => vars[key] ?? `{{${key}}}`)
    console.log(notification)
    // 'Здравствуйте, Иван! Ваш заказ #A-1234 на сумму 2500₽ готов.'
    
    // 5. Поиск всех ссылок в тексте поста
    const post = 'Смотри https://github.com/user/repo и http://docs.example.org/api'
    const urlRe = /https?:\/\/[\w./%-]+/g
    console.log(post.match(urlRe))
    // ['https://github.com/user/repo', 'http://docs.example.org/api']

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

    На GitHub при создании PR система автоматически находит упоминания задач вида #1234, email-адреса, ссылки и превращает их в кликабельные элементы. В Notion поиск работает по всему тексту. В VS Code функция «найти и заменить» с регулярными выражениями позволяет за секунды переименовать паттерн во всём проекте. Всё это — регулярные выражения.

    Какую проблему решает

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

    На основе предыдущих уроков

  • Строки — методы строк: replace, match, includes
  • Типы данных — RegExp как тип данных
  • Создание RegExp

    const re1 = /hello/i                    // литеральный синтаксис — предпочтительный
    const re2 = new RegExp('hello', 'i')    // конструктор — нужен когда паттерн динамический
    
    // Динамический паттерн — только через конструктор
    const searchTerm = 'javascript'
    const re3 = new RegExp(searchTerm, 'gi')

    Флаги

  • g — global: найти все вхождения, не только первое
  • i — case-insensitive: игнорировать регистр
  • m — multiline: ^ и $ работают для каждой строки текста
  • Основные паттерны

    .          // любой символ кроме переноса строки
    \\d         // цифра [0-9]
    \\D         // не цифра
    \\w         // буква, цифра или _ [a-zA-Z0-9_]
    \\W         // не \\w
    \\s         // пробельный символ (пробел, таб, перенос)
    \\S         // не пробел
    
    +          // 1 или более
    *          // 0 или более
    ?          // 0 или 1 (необязательный)
    {n}        // ровно n
    {n,m}      // от n до m
    [abc]      // один из символов a, b, c
    [a-z]      // диапазон
    [^abc]     // не a, b, c
    ^          // начало строки
    $          // конец строки
    (a|b)      // a или b

    Методы

    /\\d+/.test('abc123')               // true — есть ли совпадение
    'abc123'.match(/\\d+/)             // ['123'] — первое совпадение
    'abc123def456'.match(/\\d+/g)      // ['123', '456'] — все совпадения (флаг g)
    'hello'.replace(/l/g, 'r')          // 'herro'
    'a1b2c3'.replace(/\\d/g, '_')     // 'a_b_c_'
    
    // matchAll — итерируемый список всех совпадений с подробностями
    const all = [...'a1b22c333'.matchAll(/\\d+/g)]
    all.map(m => m[0])  // ['1', '22', '333']

    Группы захвата

    // Именованные группы — предпочтительны в сложных паттернах
    const dateRe = /(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})/
    const m = '2024-06-20'.match(dateRe)
    m.groups  // { year: '2024', month: '06', day: '20' }
    
    // Обычные группы (нумерованные)
    const m2 = '2024-06-20'.match(/(\\d{4})-(\\d{2})-(\\d{2})/)
    m2[1]  // '2024'
    m2[2]  // '06'

    Замена с функцией-обработчиком

    // Шаблонизатор: {{name}} → реальное значение
    const template = 'Привет, {{name}}! Заказ {{orderId}} готов.'
    const vars = { name: 'Иван', orderId: '#1234' }
    
    const result = template.replace(/\{\{(\w+)\}\}/g, (_, key) => vars[key] ?? _)
    // 'Привет, Иван! Заказ #1234 готов.'

    Типичные ошибки

    Ошибка 1: забыли флаг g — match возвращает только первое вхождение

    const text = 'тел: +7-916-123, доп: +7-499-456'
    
    // Без g — только первый номер
    text.match(/\\+7[\\d-]+/)    // ['+7-916-123']
    
    // С g — все номера
    text.match(/\\+7[\\d-]+/g)  // ['+7-916-123', '+7-499-456']

    Ошибка 2: test() с флагом g меняет lastIndex

    const re = /\\d+/g  // флаг g сохраняет позицию между вызовами!
    
    re.test('abc123')   // true
    re.test('abc123')   // false — поиск начался с позиции 6 (после '123')
    re.test('abc123')   // true — позиция сбросилась
    
    // Безопасно: используй новый RegExp или /паттерн/.test() без g для test()
    /\\d+/.test('abc123')   // true — всегда работает

    Ошибка 3: не экранировали спецсимволы

    // Хотим найти точку буквально, но . в regex — любой символ
    '3.14'.match(/3.14/)   // ['3.14'] — совпадёт, но и '3x14' тоже!
    '3.14'.match(/3\\.14/) // ['3.14'] — только с точкой
    
    // Хотим найти скобку
    'fn(x)'.match(/fn\\(x\\)/)  // нужно экранировать

    В реальных проектах

  • Валидация форм: email, телефон, пароль, СНИЛС, ИНН
  • Шаблонизаторы: замена {{переменных}} в HTML-шаблонах
  • Парсинг: извлечение цен, дат, ссылок из текста
  • Форматирование: номер карты 4-4-4-4, телефон +7 (999) 123-45-67
  • Линтеры и Code Review: поиск запрещённых паттернов в коде
  • Примеры

    Валидация форм, извлечение данных и шаблонизатор — реальные задачи

    // 1. Валидация email и российского телефона
    const emailRe = /^[\w.-]+@[\w.-]+\.[a-z]{2,}$/i
    console.log(emailRe.test('user@example.com'))   // true
    console.log(emailRe.test('bad@.com'))           // false
    console.log(emailRe.test('no-at-sign'))         // false
    
    const phoneRe = /^\+7\d{10}$/
    console.log(phoneRe.test('+79161234567'))  // true
    console.log(phoneRe.test('89161234567'))   // false — нет +7
    
    // 2. Извлечение всех чисел из текста (корзина магазина)
    const receipt = 'Товары: 3 шт., итого 1500 руб., скидка 10%, к оплате 1350 руб.'
    const numbers = receipt.match(/\d+/g)
    console.log(numbers)  // ['3', '1500', '10', '1350']
    
    // 3. Именованные группы — парсинг даты из строки
    const dateRe = /(?<day>\d{2})\.(?<month>\d{2})\.(?<year>\d{4})/
    const match = '25.12.2024'.match(dateRe)
    const { day, month, year } = match.groups
    console.log(`${year}-${month}-${day}`)  // '2024-12-25'
    
    // 4. Шаблонизатор уведомлений (как в SendPulse или Unisender)
    const template = 'Здравствуйте, {{name}}! Ваш заказ {{orderId}} на сумму {{total}}₽ готов.'
    const vars = { name: 'Иван', orderId: '#A-1234', total: '2500' }
    
    const notification = template.replace(/\{\{(\w+)\}\}/g, (_, key) => vars[key] ?? `{{${key}}}`)
    console.log(notification)
    // 'Здравствуйте, Иван! Ваш заказ #A-1234 на сумму 2500₽ готов.'
    
    // 5. Поиск всех ссылок в тексте поста
    const post = 'Смотри https://github.com/user/repo и http://docs.example.org/api'
    const urlRe = /https?:\/\/[\w./%-]+/g
    console.log(post.match(urlRe))
    // ['https://github.com/user/repo', 'http://docs.example.org/api']

    Задание

    Напиши три функции для системы обработки пользовательских данных: validateEmail(str) — проверяет корректность email; validatePhone(str) — проверяет российский номер в формате +7XXXXXXXXXX (ровно 10 цифр после +7); extractHashtags(str) — достаёт все хэштеги из текста поста (слова начинающиеся с #, содержащие буквы/цифры/подчёркивание).

    Подсказка

    Email: /^[\\w.-]+@[\\w.-]+\\.[a-z]{2,}$/i — начало, буквы/точка/дефис, @, домен, точка, зона. Phone: /^\\+7\\d{10}$/ — строго +7 и ровно 10 цифр. Hashtags: /#[\\w]+/g — символ # за которым идут буквы/цифры/подчёркивание.

    Загружаем среду выполнения...
    Загружаем AI-помощника...