Поведение регулярных выражений с переносами строк в JavaScript
Обработка переносов строк в регулярных выражениях — один из самых запутанных аспектов сопоставления шаблонов. Поведение метасимволов таких как ., ^ и $ радикально меняется в зависимости от флагов regex, и разные языки программирования по-разному реализуют обработку переносов строк.
Это руководство фокусируется на поведении переносов строк в JavaScript regex, охватывая флаги DotAll (s) и Multiline (m), распространенные ошибки и практические паттерны для сопоставления текста, охватывающего несколько строк.
Метасимвол точка (.)
По умолчанию, точка (.) соответствует любому символу, кроме символов переноса строки (\n, \r).
Поведение по умолчанию
const text = "Привет\nМир";
const regex = /Привет.Мир/;
console.log(regex.test(text)); // false
// Точка не соответствует символу переноса строкиИспользование флага DotAll (s)
Флаг s (также называемый "dotAll" или "single-line") заставляет точку соответствовать символам переноса строки:
const text = "Привет\nМир";
const regex = /Привет.Мир/s; // Обратите внимание на флаг 's'
console.log(regex.test(text)); // true
// Теперь точка соответствует переносу строкиВажно: Флаг s был введен в ES2018. Для старых браузеров используйте [\s\S] или [^] вместо ..
Якоря: ^ и $
Якоря ^ (начало) и $ (конец) также ведут себя по-разному с переносами строк.
Поведение по умолчанию
Без флага multiline, ^ соответствует началу всей строки, а $ соответствует концу:
const text = "Строка 1\nСтрока 2\nСтрока 3";
const regex = /^Строка 2/;
console.log(regex.test(text)); // false
// ^Строка 2 ищет "Строка 2" в начале строки
// но строка начинается с "Строка 1"Использование флага Multiline (m)
Флаг m меняет ^ и $ для соответствия началу и концу каждой строки:
const text = "Строка 1\nСтрока 2\nСтрока 3";
const regex = /^Строка 2/m; // Обратите внимание на флаг 'm'
console.log(regex.test(text)); // true
// Теперь ^Строка 2 соответствует "Строка 2" в начале второй строкиПрактический пример: Извлечение строк
const log = `
[INFO] Приложение запущено
[ERROR] Ошибка подключения к БД
[INFO] Повтор попытки...
[ERROR] Таймаут после 30с
`;
// Извлечь все строки ERROR
const errorRegex = /^\[ERROR\].*$/gm;
const errors = log.match(errorRegex);
console.log(errors);
// ["[ERROR] Ошибка подключения к БД", "[ERROR] Таймаут после 30с"]Распространенные ошибки
Ошибка 1: Путаница флагов s и m
Названия не интуитивны:
- Флаг
s= "режим одной строки", но заставляет.соответствовать переносам строк (полезно для многострочного сопоставления) - Флаг
m= "многострочный режим" и влияет на якоря^и$
Эти флаги независимы и могут комбинироваться:
const regex = /^.*$/sm; // Оба флага вместе
// s: точка соответствует переносам строк
// m: ^ и $ соответствуют границам строкОшибка 2: Разные символы переноса строки
Разные операционные системы используют разные последовательности переноса строки:
- Unix/Linux:
\n(LF) - Windows:
\r\n(CRLF) - Старый Mac:
\r(CR)
Чтобы соответствовать любому варианту переноса строки:
// Соответствие любой последовательности переноса строки
const anyNewline = /\r?\n|\r/g;
// Разделение по любому переносу строки
const lines = text.split(/\r?\n|\r/);
// Альтернатива: используйте более полный паттерн
const lines2 = text.split(/\r\n|\n|\r/);Ошибка 3: Жадное сопоставление через строки
При использовании флага s, жадные квантификаторы могут захватить слишком много:
const html = `
<div>Первый</div>
<div>Второй</div>
<div>Третий</div>
`;
// Попытка найти отдельные блоки <div>
const greedy = /<div>.*<\/div>/s;
console.log(html.match(greedy)[0]);
// Соответствует от первого <div> до ПОСЛЕДНЕГО </div> - вся строка!
// Используйте нежадное сопоставление
const nonGreedy = /<div>.*?<\/div>/gs;
console.log(html.match(nonGreedy));
// ["<div>Первый</div>", "<div>Второй</div>", "<div>Третий</div>"]Практические паттерны
Паттерн 1: Соответствие всему между маркерами
// Извлечь содержимое между маркерами START и END (включая переносы строк)
const text = `
Некоторый текст
START
Важное содержимое
охватывающее несколько
строк
END
Еще текст
`;
const pattern = /START([\s\S]*?)END/;
const match = text.match(pattern);
console.log(match[1].trim());
// "Важное содержимое\nохватывающее несколько\nстрок"
// Современная альтернатива с флагом s
const pattern2 = /START(.*?)END/s;Паттерн 2: Соответствие строк, начинающихся с определенного текста
const log = `
2026-01-11 10:00:00 INFO Сервер запущен
2026-01-11 10:00:15 ERROR Ошибка подключения
2026-01-11 10:00:20 INFO Повтор попытки
2026-01-11 10:00:25 ERROR Таймаут
`;
// Получить все строки ERROR
const errors = log.match(/^.*ERROR.*$/gm);
console.log(errors);Тестирование ваших паттернов
При работе со сложными паттернами переносов строк, используйте DevToys Pro RegEx Tester для:
- Тестирования паттернов с разными комбинациями флагов
- Визуализации соответствий через несколько строк
- Отладки жадного и нежадного поведения сопоставления
- Экспериментов с разными последовательностями переносов строк
Лучшие практики
1. Будьте явными о флагах
// Плохо: неясно, какое поведение вы хотите
const pattern = /start.*end/;
// Хорошо: явно о обработке переносов строк
const pattern = /start.*?end/s; // флаг s: точка соответствует переносам
const pattern2 = /^start.*end$/m; // флаг m: ^ и $ для каждой строки2. Используйте [\s\S] для широкой совместимости
// Если поддержка флага s неопределенна, используйте [\s\S]
const pattern = /start([\s\S]*?)end/;
// [\s\S] означает "любой пробельный или непробельный" = любой символ3. Нормализуйте окончания строк когда возможно
// Нормализовать к \n перед обработкой
const normalized = text.replace(/\r\n|\r/g, '\n');
// Теперь можно использовать более простые паттерны
const lines = normalized.split('\n');Заключение
Понимание поведения переносов строк в регулярных выражениях критично для задач обработки текста. Ключевые выводы:
- Флаг
sзаставляет.соответствовать переносам строк (режим dotAll) - Флаг
mзаставляет^и$соответствовать границам строк (многострочный режим) - Эти флаги независимы и могут комбинироваться:
/pattern/sm - Используйте
[\s\S]или[^]для широкой совместимости вместо.с флагомs - Учитывайте разные последовательности переносов строк в разных ОС
- Используйте нежадные квантификаторы чтобы не захватить слишком много
Для тестирования и отладки regex паттернов, особенно связанных с переносами строк, используйте RegEx Tester для визуализации соответствий и экспериментов с разными комбинациями флагов.