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

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

Предположим, мы хотим найти все числа, которые следуют за ценой в длинной статье, а не найти все числа. Регулярное выражение может сопоставляться со всеми числами, но с помощью утверждений можно точно указать:Я сопоставляю только числа, которые непосредственно следуют за ценами。

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

Особенности утверждений

  1. Нулевая ширина: не занимает позиции совпадающих символов
  2. Проверка условия: только проверяет, выполнено ли определённое условие
  3. Не влияет на результат сопоставления: служит только ограничением для сопоставления

Пример

// Пример: сопоставить foo, за которым следует bar
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

const text = "Цена товара A: 299 yuan, цена товара B: 599 yuan, доставка: 20 yuan.";

// Позитивная опережающая проверка
// Найти одну или несколько цифр, но потребовать, чтобы справа сразу следовало "yuan"
// Сам "yuan" не входит в результат совпадения
const pattern = /\d+(?=元)/g;

const matches = text.match(pattern);

console.log("Найденные числа цен:", matches);
// Вывод: ['299', '599', '20']

Пример: Python

import re

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']

Объяснение кода:

  1. \d+— это основное выражение, соответствующее одной или нескольким цифрам.
  2. (?=yuan)— это утверждение, оно проверяет\d+у совпавшей строки цифрсправа, непосредственно ли за ней следует символ "yuan".
  3. Движок сначала находит299, затем смотрит вправо, находит "yuan", утверждение выполняется, поэтому299записывается.
  4. Затем продолжает находить599, справа находится "yuan", проверка успешна, записывает его.
  5. Находит20, справа находится "yuan", срабатывает, записывает его. В итоге мы получаем только числовую часть.

Пример кода 2: Проверка сложных паролей

Требование: пароль должен содержать как минимум одну заглавную букву, одну строчную букву и одну цифру.

Пример: JavaScript

function validatePassword(password) {
  // Несколько позитивных опережающих утверждений
  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

import re

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

// Пример 1: Негативная опережающая проверка
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

// Пример 1: Сопоставить сумму после $ или £ (классический, семантически корректный)
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 text = "Сегодня температура -5 градусов, завтра она поднимется до 3 градусов, послезавтра будет -1 градус, а температура в помещении остаётся 22 градуса.";

// Негативная ретроспективная проверка
// Сопоставить цифры, но потребовать, чтобы слева не мог стоять знак минус -
const pattern = /(?<!-)\b\d+\b/g;

const matches = text.match(pattern);

console.log("Неотрицательные целые числа:", matches);
// Вывод: ['3', '22']
// Слева от -5 и -1 стоит '-', утверждение не выполняется, совпадений нет

Комплексное применение и блок-схема

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

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

Переключение вкладок
Позитивное утверждение просмотра вперёд
Негативное утверждение просмотра вперёд
Позитивное утверждение просмотра назад
Негативное утверждение просмотра назад
Комплексная практика
Интерактивное тестирование
Позитивное утверждение просмотра вперёд
Позитивное утверждение просмотра вперёд
(?=pattern)
Негативное утверждение просмотра вперёд
Негативное утверждение просмотра вперёд
(?!pattern)
Позитивное утверждение просмотра назад
Позитивное утверждение просмотра назад
(?<=pattern)
Негативное утверждение просмотра назад
Негативное утверждение просмотра назад
(?<!pattern)
Комплексная практика
Комплексный пример для практики
Интерактивный тест
Область интерактивного тестирования

Мини-викторина по утверждениям в регулярных выражениях

1. Для шаблона `Python(?=3)` в строке `"我喜欢Python3和Python2"`, какой из следующих вариантов верен?

2. Какое утверждение можно использовать, чтобы сопоставить цифру `7`, перед которой не стоит буква `q`?

3. Каков результат сопоставления шаблона `(?<=@)\w+` в строке `"联系 @admin 和 [email protected]"`?

4. Каковы основные особенности утверждений? (Выберите несколько вариантов)

Другие расширения