Регулярные выражения — утверждения
Утверждения — это метасимволы в регулярных выражениях, используемые для указания позиций сопоставления. Они не сопоставляются ни с какими реальными символами, а скорее указывают позиции между символами.
Предположим, мы хотим найти все числа, которые следуют за ценой в длинной статье, а не найти все числа. Регулярное выражение может сопоставляться со всеми числами, но с помощью утверждений можно точно указать:Я сопоставляю только числа, которые непосредственно следуют за ценами。
В этой статье систематически объясняются четыре основных типа утверждений в регулярных выражениях: позитивная опережающая проверка, негативная опережающая проверка, позитивная ретроспективная проверка и негативная ретроспективная проверка.
Особенности утверждений
- Нулевая ширина: не занимает позиции совпадающих символов
- Проверка условия: только проверяет, выполнено ли определённое условие
- Не влияет на результат сопоставления: служит только ограничением для сопоставления
Пример
const regex = /foo(?=bar)/;
console.log(regex.test("foobar")); // true
console.log(regex.test("food")); // false
Типы утверждений
Утверждения в регулярных выражениях в основном делятся на две категории и четыре типа:
| Тип утверждения | Синтаксис регулярного выражения | Псевдоним | Направление проверки | Ожидаемое условие | Простое объяснение |
|---|---|---|---|---|---|
| Позитивный просмотр вперёд | (?=pattern) |
Позитивный просмотр вперёд | Вправо (вперёд) | Exists pattern |
Позиция, которую я ищу, еёправая сторона должнабыть... |
| Негативный просмотр вперёд | (?!pattern) |
Негативный просмотр вперёд | Вправо (вперёд) | Не существует pattern |
Позиция, которую я ищу, еёправая сторона не должнабыть... |
| Позитивный просмотр назад | (?<=pattern) |
Позитивный просмотр назад | Влево (назад) | Exists pattern |
Позиция, которую я ищу, еёлевая сторона должнабыть... |
| Негативный просмотр назад | (?<!pattern) |
негативный просмотр назад | Влево (назад) | Не существует pattern |
Позиция, которую я ищу, еёлевая сторона не должнабыть... |
Позитивный просмотр вперёд(?=...)
Позитивный просмотр вперёдИспользуется для сопоставления позиции, такой что сразу после этой позиции (справа) должен появиться указанный шаблон....。
Синтаксис и параметры
- Синтаксис:
(?=pattern) - Функция: проверяет, совпадает ли правая сторона текущей позиции
pattern. Если совпадает, утверждение выполняется, и движок возвращается к текущей позиции, чтобы продолжить последующее сопоставление. - Ключевые характеристики:Нулевая ширина, то есть он только проверяет и не "потребляет" никаких символов.
patternСодержимое внутри утверждения не станет частью окончательного результата сопоставления.
Пример кода 1: Извлечение чисел цен
Предположим, у нас есть фрагмент текста, и нам нужно извлечь всецены:числовые значения сумм после них.
Пример: JavaScript
// Позитивная опережающая проверка
// Найти одну или несколько цифр, но потребовать, чтобы справа сразу следовало "yuan"
// Сам "yuan" не входит в результат совпадения
const pattern = /\d+(?=元)/g;
const matches = text.match(pattern);
console.log("Найденные числа цен:", matches);
// Вывод: ['299', '599', '20']
Пример: Python
text = "Цена товара A: 299 yuan, цена товара B: 599 yuan, доставка: 20 yuan."
# Используйте позитивную опережающую проверку
# Найти одну или несколько цифр (\d+), но потребовать, чтобы справа от этого числа сразу следовало "yuan"
# Примечание: сам "yuan" не будет включён в результат сопоставления
pattern = r'\d+(?=元)'
matches = re.findall(pattern, text)
print("Совпавшие числа цен:", matches)
# Вывод: Совпавшие числа цен: ['299', '599', '20']
Объяснение кода:
\d+— это основное выражение, соответствующее одной или нескольким цифрам.(?=yuan)— это утверждение, оно проверяет\d+у совпавшей строки цифрсправа, непосредственно ли за ней следует символ "yuan".- Движок сначала находит
299, затем смотрит вправо, находит "yuan", утверждение выполняется, поэтому299записывается. - Затем продолжает находить
599, справа находится "yuan", проверка успешна, записывает его. - Находит
20, справа находится "yuan", срабатывает, записывает его. В итоге мы получаем только числовую часть.
Пример кода 2: Проверка сложных паролей
Требование: пароль должен содержать как минимум одну заглавную букву, одну строчную букву и одну цифру.
Пример: JavaScript
// Несколько позитивных опережающих утверждений
const pattern = /^(?=.*[A-Z])(?=.*[a-z])(?=.*\d).{8,}$/;
// ^ Начало строки
// (?=.*[A-Z]) Справа должна быть хотя бы одна заглавная буква
// (?=.*[a-z]) Справа должна быть хотя бы одна строчная буква
// (?=.*\d) Справа должна быть хотя бы одна цифра
// .{8,} Общая длина не менее 8
// $ Конец строки
return pattern.test(password);
}
// Тестовые данные
const passwords = ["Weak", "strong123", "STRONG123", "Strong123"];
passwords.forEach(pwd => {
console.log(`Пароль'${pwd}'действителен: ${validatePassword(pwd)}`);
});
// Вывод:
// Пароль 'Weak' действителен: false
// Пароль 'strong123' действителен: false
// Пароль 'STRONG123' действителен: false
// Пароль 'Strong123' действителен: true
Пример: Pyhton
def validate_password(password):
# Используйте несколько положительных опережающих проверок, чтобы проверять условия по отдельности
pattern = r'^(?=.*[A-Z])(?=.*[a-z])(?=.*\d).{8,}$'
# ^ обозначает начало строки
# (?=.*[A-Z]) Проверка: при взгляде вправо от текущей позиции (начала) после любых символов (.*) должна быть заглавная буква
# (?=.*[a-z]) Проверка: аналогично, при взгляде вправо от начала должна быть строчная буква
# (?=.*\d) Проверка: при взгляде вправо от начала должна быть цифра
# .{8,} Основное выражение: совпадение любого символа не менее 8 раз (требование к общей длине)
# $ обозначает конец строки
if re.match(pattern, password):
return True
else:
return False
# Тестовые данные
passwords = ["Weak", "strong123", "STRONG123", "Strong123"]
for pwd in passwords:
print(f"Пароль '{pwd}' корректен: {validate_password(pwd)}")
# Вывод:
# Пароль 'Weak' корректен: False # Недостаточная длина и отсутствует цифра
# Пароль 'strong123' корректен: False # Отсутствует заглавная буква
# Пароль 'STRONG123' действителен: False # Отсутствует строчная буква
# Пароль 'Strong123' действителен: True # Соответствует всем условиям
Негативный просмотр вперёд(?!...)
Негативная опережающая проверкаПротивоположность позитивной опережающей проверке. Она соответствует позиции, такой что после этой позиции (справа) указанный шаблонне долженнепосредственно следовать...。
Синтаксис и параметры
- Синтаксис:
(?!pattern) - Назначение: Проверить,не совпадает ли правая сторона текущей позиции
pattern. Если совпадения нет, утверждение выполняется. - Ключевые особенности: Также нулевой ширины.
Пример кода: Поиск слов, не заканчивающихся на ing
В предложении найдите все слова,ingкоторые не заканчиваются на.
Пример: JavaScript
const text1 = "playing swimming run walk jumping sing";
const pattern1 = /\b\w+(?<!ing)\b/g;
console.log(text1.match(pattern1));
// ['run', 'walk', 'sing']
// Пример 2: Сопоставить слова, не заканчивающиеся на q (семантически корректная версия)
const text2 = "I like faq apple Iraq you banana q";
const pattern2 = /\b\w*[^q\W]\b/g;
// Значение:
// [^q\W] Последний символ слова — не q
// Лучше соответствует реальному требованию «не заканчивается на q», чем (?!q)
console.log(text2.match(pattern2));
// ['I', 'like', 'apple', 'you', 'banana']
// Пример 3: Сопоставить имена файлов, не заканчивающиеся на .js
const files = "index.js app.ts config.json main.js readme.md";
const pattern3 = /\b\w+\.(?!js\b)\w+\b/g;
// Значение: \.(?!js\b) После точки не должно следовать js
console.log(files.match(pattern3));
// ['app.ts', 'config.json', 'readme.md']
Позитивный просмотр назад(?<=...)
Утверждение положительного просмотра назадИспользуется для сопоставления позиции, в которой указанный шаблон должен непосредственно находиться перед этой позицией (слева)....。
Примечание: не все движки регулярных выражений языков программирования поддерживают утверждения просмотра назад. JavaScript полностью поддерживает их только начиная с ES2018, а в Pythonreмодуль `re` поддерживает их.
Синтаксис и параметры
- Синтаксис:
(?<=pattern) - Назначение: Проверить, совпадает ли левая сторона текущей позиции
pattern. Если совпадает, утверждение срабатывает. - Ключевые особенности: Нулевая ширина.
patternДолжно иметь фиксированную длину (не может быть*или+квантификатором переменной длины и т. п. в некоторых реализациях).
Пример кода: Извлечение сумм после символов валют
Извлеките цифры после знака доллара или фунта, но не включая сам знак.
Пример: JavaScript
const text1 = "Price: $199, £89, ¥1200";
const pattern1 = /(?<=\$|£)\d+/g;
// Значение:
// (?<=\$|£) Левая часть должна быть $ или £
// \d+ Сопоставить сами цифры
console.log(text1.match(pattern1));
// ['199', '89']
// Пример 2: Сопоставить число после двоеточия
const text2 = "port:8080 pid:1234 uid:1000";
const pattern2 = /(?<=:)\d+/g;
// Левая часть должна быть :
console.log(text2.match(pattern2));
// ['8080', '1234', '1000']
// Пример 3: Сопоставить минорную версию в номере версии (после номера мажорной версии)
const text3 = "v1.2 v2.15 v10.3";
const pattern3 = /(?<=v\d+\.)\d+/g;
// Левая сторона должна быть v + цифры + точка
console.log(text3.match(pattern3));
// ['2', '15', '3']
// Пример 4: Найти имя пользователя после @ (не включая @)
const text4 = "привет @alice и @bob_smith";
const pattern4 = /(?<=@)[a-zA-Z_]\w*/g;
console.log(text4.match(pattern4));
// ['alice', 'bob_smith']
// Пример 5: Найти номер главы в китайском "第 X 章"
const text5 = "第1章 第12章 第3章";
const pattern5 = /(?<=第)\d+(?=章)/g;
// Ограничить обе стороны одновременно, понятнее
console.log(text5.match(pattern5));
// ['1', '12', '3']
Негативный просмотр назад(?<!...)
Негативная ретроспективная проверкаЭто противоположность позитивной ретроспективной проверки. Она соответствует позиции, где перед ней (слева),указанный шаблон не можетпоявиться непосредственно....。
Синтаксис и параметры
- Синтаксис:
(?<!pattern) - Назначение: Проверить, что левая сторона текущей позициине совпадает
pattern。 - Ключевые особенности: Нулевая ширина, обычно требует
patternфиксированной длины.
Пример кода: поиск неотрицательных целых чисел
В абзаце найдите все целые числа, которые не являются отрицательными (т. е. нет знака минус слева-от цифры).
Пример: JavaScript
// Негативная ретроспективная проверка
// Сопоставить цифры, но потребовать, чтобы слева не мог стоять знак минус -
const pattern = /(?<!-)\b\d+\b/g;
const matches = text.match(pattern);
console.log("Неотрицательные целые числа:", matches);
// Вывод: ['3', '22']
// Слева от -5 и -1 стоит '-', утверждение не выполняется, совпадений нет
Комплексное применение и блок-схема
Чтобы более наглядно понять рабочий процесс четырёх утверждений, мы можем использовать следующую блок-схему для иллюстрации сложного процесса сопоставления, содержащего несколько утверждений:

Описание блок-схемы: Диаграмма показывает, как движок регулярных выражений обрабатывает утверждения во время сопоставления. Суть в том, чтоутверждения — это независимые проверочные узлы. Движок втекущей позиции, где основное выражение пытается найти совпадение,проверяет контекст слева или справа в зависимости от типа утверждения. Только когда все условия утверждения выполнены, сопоставление может продолжиться вперёд и «поглотить» символы; в противном случае движок выполняет откат, чтобы попробовать другие возможности, или объявляет о неудаче сопоставления.