Регулярные выражения на бесконечном вводе

Я хотел бы использовать регулярные выражения для анализа данных, полученных из сокета. Я написал собственный итератор сокетов, чтобы я мог передавать данные функциям регулярных выражений std. Имейте в виду, что данные теоретически могут никогда не закончиться, сокет не закрывается после отправки полного запроса, поскольку клиент ожидает ответа и, возможно, будущего взаимодействия.

Представим, что у нас очень простой протокол, запросы состоят либо из START, либо из STOP.
Настоящий протокол, конечно, намного сложнее, но для примера этого достаточно.

// A simple regular expression to parse this could be defined like so:
static const std::regex re("^(START|STOP)");
// And parsed using:
std::regex_match(begin, end, result, re); // 1
// or using regex_search
std::regex_search(begin, end, result, re); // 2

Допустим, клиент отправляет слово START, ждет 5 секунд, затем отправляет другой символ, например X. В этом случае метод №1 будет ждать 5 секунд, прежде чем вернет false. Теперь представьте, что клиент ничего не отправляет после исходного сообщения START, метод №1 никогда не вернется. Что касается метода № 2: предположим, что ваш ввод - XSTART, синтаксический анализатор, похоже, не понимает, что действительное совпадение никогда не будет найдено, потому что регулярное выражение начинается с ^, и поскольку ввод бесконечен, он никогда не завершится. Таким образом, в конечном итоге метод №1 правильно определяет недопустимые запросы, в то время как метод №2 правильно определяет допустимые запросы, но метод №1 застревает в бесконечном цикле при правильном запросе, а метод №2 застревает при недопустимом запросе.

Этот Минимальный, полный и проверяемый пример демонстрирует проблему:

#include <stdio.h>
#include <stdint.h>
#include <iterator>
#include <vector>
#include <regex>

// stdin iterator that goes against all good
// programming practices for the sake of simplicity
class stdin_iter : public std::iterator<std::bidirectional_iterator_tag, char> {
    static std::vector<char> buf;
    size_t i;
public:
    stdin_iter() : i(SIZE_MAX) {}
    stdin_iter(size_t i) : i(i) {}
    bool operator==(const stdin_iter& o) const { return i == o.i; }
    bool operator!=(const stdin_iter& o) const { return i != o.i; }
    value_type operator*() const {
        while (i >= buf.size()) buf.push_back(getc(stdin));
        return buf[i];
    }
    stdin_iter& operator++() { i++; return *this; }
    stdin_iter operator++(int) { stdin_iter r = *this; i++; return r; }
    stdin_iter& operator--() { i--; return *this; }
    stdin_iter operator--(int) { stdin_iter r = *this; i--; return r; }
};
std::vector<char> stdin_iter::buf;

int main() {
    stdin_iter begin(0), end;
    std::regex re("^(START|STOP)");
    std::match_results<stdin_iter> result;

    //bool valid = std::regex_match(begin, end, result, re); // stuck on valid input
    //bool valid = std::regex_search(begin, end, result, re); // stuck on invalid input
    bool valid = std::regex_search(begin, end, result, re, std::regex_constants::match_continuous); // mostly works

    if (valid) printf("valid: %s\n", result[1].str().c_str());
    else printf("invalid\n");
}

Одно из решений - добавить искусственный конец к данным после, например, секунды бездействия. Но это значительно увеличивает время отклика и кажется неправильным. Еще одно решение - написать собственный синтаксический анализатор регулярных выражений, но изобретать колесо для такой простой проблемы кажется излишним. Есть ли лучший способ заставить эту работу работать?

Непонятно, что именно вам нужно. Чтобы соответствовать строке, которая является либо Start, либо Stop, подойдет регулярное выражение "Start|Stop" с regex_match.

Wiktor Stribiżew 09.12.2018 20:17

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

Sami Hult 09.12.2018 20:22

@SamiHult Разбор бесконечно длинного ввода отлично подходит, если регулярное выражение соответствует только конечному количеству символов. Я ищу совпадения не в начале строки, а в начале сообщения.

DutChen18 09.12.2018 20:28

Да, если вы можете рассчитывать на завершение алгоритма (контролируемый ввод) или не заботитесь о том, завершится он или нет. Ваша практическая проблема может заключаться в том, что regex_search попытается сопоставить все вхождения в вашем вводе и для вечного ввода, который не будет завершен. Вы пробовали с флагом match_any?

Sami Hult 09.12.2018 21:03
match_any, похоже, не помогает, но, просматривая некоторые другие флаги, я обнаружил match_continuous, который делает именно то, что я хочу.
DutChen18 09.12.2018 21:50

Не бывает бесконечного ввода. Со временем это закончится. Вопрос в том, возьмете ли вы под свой контроль этот финал или позволите судьбе решать?

Lightness Races in Orbit 09.12.2018 22:39

Однако я не получаю информации о бесконечном цикле. минимальный воспроизводимый пример действительно поможет объяснить вашу проблему.

Lightness Races in Orbit 09.12.2018 22:40

@LightnessRacesinOrbit Теоретически ввод может продолжаться вечно, хотя это недопустимый запрос, надежная реализация должна обрабатывать все случаи. Мой вопрос больше касается определения того, действителен ли ввод, даже если вы не уверены, что получили его все, STAXдолжен немедленно будет определен как недопустимый без чтения остальной части ввода, а STARTдолжен будет считаться действительным без проверки того, после него идет еще один символ, поэтому он не может быть ни START, ни STOP. Оба вышеуказанных метода не работают по крайней мере в одном из этих случаев.

DutChen18 09.12.2018 23:35

На самом деле, если подумать, я полагаю, вы могли бы передать некоторые итераторы, которые представляют бесконечный диапазон. Если вы это делаете, это одна из вещей, которые я хочу видеть в вашем минимальный воспроизводимый пример. Обратите внимание, что это не удовлетворяет определению «диапазона» (если begin никогда не достигнет end) и, таким образом, вероятно, в любом случае имеет UB

Lightness Races in Orbit 10.12.2018 00:01

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

DutChen18 10.12.2018 00:05

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

Lightness Races in Orbit 10.12.2018 00:11

@LightnessRacesinOrbit Я добавил минимальный, полный и проверяемый пример. Я чувствую, что действительно превзошел себя с этим.

DutChen18 10.12.2018 00:37

Кстати, std::istream_iterator уже есть

Lightness Races in Orbit 10.12.2018 02:25

Используйте одно регулярное выражение, соответствующее всем вашим сценариям. Поместите каждого в чередование, заключенное в группу захвата. Одновременно будет соответствовать только 1 группа, что дает вам логику определения совпадений с помощью индекса итератора.

user557597 10.12.2018 03:42

@LightnessRacesinOrbit std::istream_iterator - это InputIterator, для регулярных выражений требуется BidirectionalIterator.

DutChen18 10.12.2018 13:00

@ DutChen18 О, так и есть. Что ж, это уже должна быть подсказка;)

Lightness Races in Orbit 10.12.2018 13:10

@LightnessRacesinOrbit намек на что? Моя реализация работает с std::regex_constants::match_continuous, так что вы хотите сказать?

DutChen18 10.12.2018 13:14

Этот std::regex не предназначен для такой тесной связи с входным потоком. Обратите внимание, что ваша реализация в конечном итоге буферизует все входные данные.

Lightness Races in Orbit 10.12.2018 14:02

Мне пришлось использовать Create a new program, adding in only what is needed to see the problem. для моего минимального, полного и проверяемого примера, моя фактическая реализация очищает буфер после сопоставления регулярного выражения. И, честно говоря, меня не волнует, что std::regex должен делать предназначена, поскольку возможность синтаксического анализа запросов с использованием регулярных выражений приводит к получению чистого кода.

DutChen18 10.12.2018 14:14
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
19
69
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Используйте флаг std::regex_constants::match_continuous, Люк.

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