Сопоставление фиксированной позиции в строке с использованием регулярного выражения C++

Могут ли регулярные выражения C++ ссылаться на фиксированную позицию внутри строки? Я ищу обозначение, которое (вымышленно) показано в следующем примере как @n, где n — индекс следующего символа:

string = "hello12345";
re = "([a-z]+[0-9]+)(@7)(.+)";
// Match: ["hello12","345"]

Обновление: я не могу просто повторить фиксированное количество символов, потому что не знаю, сколько из них будет соответствовать [a-z] и [0-9].

Вы можете пропустить фиксированное количество символов с помощью .{n}, где n — количество пропускаемых символов.

Barmar 18.06.2024 22:22

Непонятно, что вы имеете в виду под этим совпадением. Регулярное выражение всегда соответствует непрерывной строке. Что означает ...?

Barmar 18.06.2024 22:24

@Barmar Я не могу просто повторить фиксированное количество символов, потому что не знаю, сколько из них будет соответствовать [a-z] и [0-9]. См. обновленный пример.

DYZ 18.06.2024 22:29
([a-z]{6}\d{1}|[a-z]{5}\d{2}|[a-z]{4}\d{3}|[a-z]{3}\d{4}|[a-‌​z]{2}\d{5}|[a-z]{1}\‌​d{6})(.+)? Демо
Jarod42 18.06.2024 22:43

Вам нужно сделать это в одном регулярном выражении? Почему бы просто не получить подстроку из первых 7 символов и проверить ее совпадение [a-z]+[0-9]+)

Barmar 18.06.2024 22:44

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

Ted Lyngmo 18.06.2024 22:45

@Demo Работает для двух частей, не работает (масштабируется в геометрической прогрессии) для большего.

DYZ 18.06.2024 22:45

Меня зовут не Демо ;-)

Jarod42 18.06.2024 22:46

@Бармар, думаю, ты прав; это сработает. Спасибо за обходной путь. Означает ли это, что ответ на мой вопрос отрицательный? (Нет встроенного механизма табуляции?)

DYZ 18.06.2024 22:47

Кстати: я обнаружил, что регулярные выражения в C++ полезны только тогда, когда я хочу предоставить пользователю некоторые параметры поиска или фильтрации. Если я знаю, что хочу искать при компиляции программы, то, по моему опыту, написать этот поиск с нуля будет проще и надежнее, и это всегда быстрее.

Ted Lyngmo 18.06.2024 22:47

@Jarod42 Мои извинения.

DYZ 18.06.2024 22:47

@TedLyngmo Я не могу выбирать.

DYZ 18.06.2024 22:48

@DYZ Я пытался найти решение, используя предварительный просмотр и .{7}. Но я не мог гарантировать, что совпадение [a-z]+[0-9]+ полностью находилось внутри первых семи символов.

Barmar 18.06.2024 22:49

@Barmar Я тоже пробовал просмотр вперед.

DYZ 18.06.2024 22:49

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

Barmar 18.06.2024 22:51

@Barmar Я приму ваш ответ, чтобы разделить строку и проверить ее по частям, если вы ее опубликуете.

DYZ 18.06.2024 22:52

@Barmar Как я уже сказал, я не в состоянии выбирать.

DYZ 18.06.2024 22:53

@DYZ Если вам разрешено разделить строку один раз, можно ли разделить ее дважды? Трижды?

Ted Lyngmo 18.06.2024 22:53

Какой ожидаемый ответ на "abcdefgh12"? Обратите внимание, что в вашем регулярном выражении нет привязки ^. В моем комментарии принято игнорировать первые буквы; текущие решения полностью отвергают.

Jarod42 18.06.2024 23:22

@Jarod42 Ожидаемый ответ не соответствует, поскольку в первых семи символах строки нет цифр, что будет подтверждено после взятия подстроки «abcdegf» и попытки сопоставить ее.

DYZ 18.06.2024 23:25
^(?=\D{1,6}\d)([a-z\d]{7})(.+) была бы еще одна идея, но я думаю, это не соответствует вашим требованиям к обозначениям. Однако стоит упомянуть.
bobble bubble 19.06.2024 12:31

@bobblebubble Предлагаемое вами решение не гарантирует, что в группе захвата будет ровно семь символов.

DYZ 19.06.2024 17:25

@DYZ А где нет? Я поделился демо. Первая группа захватывает [a-z\d]{7}, вторая — .+ (один или несколько любых символов). Помимо того, как он устроен и что он более эффективен, чем вариант с просмотром назад, результат должен быть таким же.

bobble bubble 19.06.2024 17:50

@bobblebubble Выражение не [a-z\d], а [a-z]+\d+. Размер каждого совпадения подвыражения неизвестен, но сумма должна быть равна 7.

DYZ 19.06.2024 18:14

@DYZ Понятно... мой вариант просмотра вперед не работает должным образом. Шаблон будет соответствовать, например, a1b12345. Спасибо за замечание, в любом случае задача интересная!

bobble bubble 19.06.2024 19:18

@DYZ Думаю, эту ошибку можно исправить, добавив еще одно условие, запрещающее число|нечисловую границу на расстоянии, например: ^(?=\D{1,6}\d)(?!.{ 1,5}\d\D)([a-z\d]{7})(.+) или более короткий вариант: ^(?!\d{7}|.{0,5}\d\ D)([a-z\d]{6}\d)(.+) (если требуется регулярное выражение и доступен просмотр назад, с ответом @TedLyngmo, безусловно, удобнее справиться)

bobble bubble 20.06.2024 10:50

Еще одна нетрадиционная идея без ретроспективного анализа: ^(?=.{7}(.+))([a-z]+\d+)\1$ (в этом случае группы перепутаны). Я пока не уверен, есть ли у него какие-либо недостатки.

bobble bubble 20.06.2024 20:11

@Thefourthbird Очень приятно! Похоже, это самый эффективный 🚀 вариант. ✨

bobble bubble 21.06.2024 09:25

@bobblebubble Думаю, все твои последние три попытки увенчались успехом.

The fourth bird 21.06.2024 15:28

Спасибо, что протестировали это до сих пор. Я уже сдалась в отчаянии, но это вселяет надежду! По крайней мере, теперь у нас есть много вариантов решения этой проблемы, даже с помощью простого просмотра вперед (я читал что-то, что стандартное регулярное выражение в С++ поддерживает область регулярных выражений ECMAScript, но без просмотра назад? Мне это непонятно).

bobble bubble 21.06.2024 15:29
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
31
268
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Не делайте все это с помощью регулярного выражения. Используйте std::string::substr(), чтобы разделить ввод на две подстроки: первые 7 символов и остальные. Затем проверьте, соответствует ли первая подстрока регулярному выражению ^[a-z]+\d+$.

Иногда так легко упустить очевидное. Кстати, вы пропустили $ в своем регулярном выражении — вы не хотите, чтобы оно в конце соответствовало мусору.

Mark Ransom 21.06.2024 00:01

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

^([a-z]+\d+)(?<!.{8})(?<=.{7})(.+)
             ^^^^^^^  ^^^^^^^
                a        b
  • a — Негативный просмотр для 8 символов.
  • b — Позитивный взгляд на 7 персонажей.

Демо на regex101.

Пример C++ с использованием boost::regex, поскольку он поддерживает обходные пути, а std::regex в настоящее время нет:

#include <boost/regex.hpp>
#include <iostream>
#include <vector>

int main() {
    boost::regex re(R"aw(^([a-z]+\d+)(?<!.{8})(?<=.{7})(.+))aw",
                    boost::regex_constants::ECMAScript);

    std::vector<std::string> strs{
        "hello12345",  //
        "a12345678",   //
        "ab12345678",  //
        "abc1234567",  //
        "abcd123456",  //
        "abcde12345",  //
        "abcdef1234",  //
        "abcdefg234",  //
        "a2cdef1234",  //
        "abcdefg1234", //
        "123456789"    //
    };

    std::cout << std::boolalpha;

    for(auto& test : strs) {
        boost::smatch what;
        bool got_match = boost::regex_match(test, what, re);
        std::cout << test << '\t' << got_match << '\n';
        if (got_match) {
            std::cout << " [\"" << what[1] << "\", \"" << what[2] << "\"]\n";
        }
    }
}

Выход:

hello12345      true
 ["hello12", "345"]
a12345678       true
 ["a123456", "78"]
ab12345678      true
 ["ab12345", "678"]
abc1234567      true
 ["abc1234", "567"]
abcd123456      true
 ["abcd123", "456"]
abcde12345      true
 ["abcde12", "345"]
abcdef1234      true
 ["abcdef1", "234"]
abcdefg234      false
a2cdef1234      false
abcdefg1234     false
123456789       false

Как отметил bobble bubble в комментарии, это можно еще упростить, закрепив положительный просмотр и пропустив отрицательный просмотр:

^([a-z]+\d+)(?<=^.{7})(.+)
                ^
              anchor added

Демо C++

Почему бы не закрепить позитивный взгляд назад, чтобы ^ начать и отказаться от негатива? (регулярное выражение101)

bobble bubble 19.06.2024 11:07

@bobblebubble Прекрасное упрощение! Спасибо!

Ted Lyngmo 19.06.2024 12:39

Если первая часть совпадения должна состоять из 1 или нескольких цифр, а вторая часть совпадения должна состоять из одного или нескольких символов от a до z, вы знаете, что первое совпадение — это символы от a до z, а последнее совпадение — это цифра.

Если n равен 7, то между ними есть 5 символов, для которых вы можете указать правило, согласно которому в позициях 2–6 не может быть цифры, за которой следует символ a-z.

Используя boost::regex, вы можете использовать:

^([a-z](?![a-z0-9]{0,4}\d[a-z])[a-z\d]{5}\d)(.+)

Узор соответствует:

  • ^ Начало строки
  • ( Захват группы 1
    • [a-z] Сопоставьте первый символ от a до z
    • (?! Негативный прогноз: утверждайте, что непосредственно справа от текущей позиции нет:
      • [a-z0-9]{0,4}\d[a-z] Сопоставьте 0–4 допустимых символа, за которыми следуют цифра и AZ.
    • ) Закройте просмотр вперед
    • [a-z\d]{5}\d Сопоставьте 5 раз разрешенные символы, за которыми следует цифра.
  • ) Закрыть группу 1
  • (.+) Захватите 1 или более персонажей в группе 2.

Демонстрация регулярных выражений


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

^(?=[a-z]+[0-9]+\b)([a-z0-9]{6}[0-9])(.+)

Демонстрация регулярных выражений

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

Основная идея — ^(?=.{n}(.*))check\1$. Итак, для вашей текущей задачи шаблон будет таким:

^(?=.{7}(.+))([a-z]+\d+)\1$

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

Другие вопросы по теме