Могут ли регулярные выражения C++ ссылаться на фиксированную позицию внутри строки? Я ищу обозначение, которое (вымышленно) показано в следующем примере как @n, где n — индекс следующего символа:
string = "hello12345";
re = "([a-z]+[0-9]+)(@7)(.+)";
// Match: ["hello12","345"]
Обновление: я не могу просто повторить фиксированное количество символов, потому что не знаю, сколько из них будет соответствовать [a-z] и [0-9].
Непонятно, что вы имеете в виду под этим совпадением. Регулярное выражение всегда соответствует непрерывной строке. Что означает ...?
@Barmar Я не могу просто повторить фиксированное количество символов, потому что не знаю, сколько из них будет соответствовать [a-z] и [0-9]. См. обновленный пример.
([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})(.+)? ДемоВам нужно сделать это в одном регулярном выражении? Почему бы просто не получить подстроку из первых 7 символов и проверить ее совпадение [a-z]+[0-9]+)
Всегда полезно получить несколько (сложных) примеров строк с крайним регистром и то, что вы хотели бы сопоставить/не сопоставить/захватить/не захватить, когда речь идет о регулярных выражениях.
@Demo Работает для двух частей, не работает (масштабируется в геометрической прогрессии) для большего.
Меня зовут не Демо ;-)
@Бармар, думаю, ты прав; это сработает. Спасибо за обходной путь. Означает ли это, что ответ на мой вопрос отрицательный? (Нет встроенного механизма табуляции?)
Кстати: я обнаружил, что регулярные выражения в C++ полезны только тогда, когда я хочу предоставить пользователю некоторые параметры поиска или фильтрации. Если я знаю, что хочу искать при компиляции программы, то, по моему опыту, написать этот поиск с нуля будет проще и надежнее, и это всегда быстрее.
@Jarod42 Мои извинения.
@TedLyngmo Я не могу выбирать.
@DYZ Я пытался найти решение, используя предварительный просмотр и .{7}. Но я не мог гарантировать, что совпадение [a-z]+[0-9]+ полностью находилось внутри первых семи символов.
@Barmar Я тоже пробовал просмотр вперед.
Поэтому я думаю, что метод Jarod42 — единственный способ, и вы получите комбинаторный взрыв, если исходный шаблон будет более сложным.
@Barmar Я приму ваш ответ, чтобы разделить строку и проверить ее по частям, если вы ее опубликуете.
@Barmar Как я уже сказал, я не в состоянии выбирать.
@DYZ Если вам разрешено разделить строку один раз, можно ли разделить ее дважды? Трижды?
Какой ожидаемый ответ на "abcdefgh12"? Обратите внимание, что в вашем регулярном выражении нет привязки ^. В моем комментарии принято игнорировать первые буквы; текущие решения полностью отвергают.
@Jarod42 Ожидаемый ответ не соответствует, поскольку в первых семи символах строки нет цифр, что будет подтверждено после взятия подстроки «abcdegf» и попытки сопоставить ее.
@bobblebubble Предлагаемое вами решение не гарантирует, что в группе захвата будет ровно семь символов.
@DYZ А где нет? Я поделился демо. Первая группа захватывает [a-z\d]{7}, вторая — .+ (один или несколько любых символов). Помимо того, как он устроен и что он более эффективен, чем вариант с просмотром назад, результат должен быть таким же.
@bobblebubble Выражение не [a-z\d], а [a-z]+\d+. Размер каждого совпадения подвыражения неизвестен, но сумма должна быть равна 7.
@DYZ Понятно... мой вариант просмотра вперед не работает должным образом. Шаблон будет соответствовать, например, a1b12345. Спасибо за замечание, в любом случае задача интересная!
@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, безусловно, удобнее справиться)
Еще одна нетрадиционная идея без ретроспективного анализа: ^(?=.{7}(.+))([a-z]+\d+)\1$ (в этом случае группы перепутаны). Я пока не уверен, есть ли у него какие-либо недостатки.
@Thefourthbird Очень приятно! Похоже, это самый эффективный 🚀 вариант. ✨
@bobblebubble Думаю, все твои последние три попытки увенчались успехом.
Спасибо, что протестировали это до сих пор. Я уже сдалась в отчаянии, но это вселяет надежду! По крайней мере, теперь у нас есть много вариантов решения этой проблемы, даже с помощью простого просмотра вперед (я читал что-то, что стандартное регулярное выражение в С++ поддерживает область регулярных выражений ECMAScript, но без просмотра назад? Мне это непонятно).





Не делайте все это с помощью регулярного выражения. Используйте std::string::substr(), чтобы разделить ввод на две подстроки: первые 7 символов и остальные. Затем проверьте, соответствует ли первая подстрока регулярному выражению ^[a-z]+\d+$.
Иногда так легко упустить очевидное. Кстати, вы пропустили $ в своем регулярном выражении — вы не хотите, чтобы оно в конце соответствовало мусору.
Вот одна идея, сочетающая в себе как отрицательный, так и положительный взгляд назад:
^([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
Почему бы не закрепить позитивный взгляд назад, чтобы ^ начать и отказаться от негатива? (регулярное выражение101)
@bobblebubble Прекрасное упрощение! Спасибо!
Если первая часть совпадения должна состоять из 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 - Производительность неплохая, она совместима и выполняет свою работу. Кроме того, он является универсальным и может быть легко адаптирован к любой длине и узору.
Вы можете пропустить фиксированное количество символов с помощью
.{n}, гдеn— количество пропускаемых символов.