Применить регулярное выражение в потоке? - PullRequest
36 голосов
/ 26 декабря 2009

Я ищу быстрый и безопасный способ применения регулярных выражений в потоках.

В Интернете я нашел несколько примеров, в которых говорилось о преобразовании каждого буфера в строку, а затем о применении Regex к строке.

У этого подхода есть две проблемы:

  • Производительность: преобразование в строки и сборщик строк - это пустая трата времени и ресурсов процессора, и, конечно, этого можно избежать, если бы существовал более естественный способ применения Regex в потоках.
  • Pure Regex support: Regex шаблон иногда может совпадать, только если объединить два буфера вместе (буфер 1 заканчивается первой частью совпадения, а буфер 2 начинается со второй части совпадения). Способ преобразования в строку не может обрабатывать этот тип сопоставления изначально, я должен предоставить больше информации, например, о максимальной длине, которой может соответствовать шаблон, он вообще не поддерживает символы регулярного выражения + и * и не будет поддерживать (неограниченное соответствие длина).

Таким образом, способ преобразования в строку не быстрый и не полностью поддерживает Regex.

Есть ли какой-либо способ / библиотека, которую можно использовать для применения Regex к потокам без преобразования в строки и с полной поддержкой Regex?

Ответы [ 3 ]

6 голосов
/ 25 мая 2016

Корпорация Intel недавно открыла библиотеку hyperscan с исходным кодом под лицензией BSD. Это высокопроизводительный, не требующий отслеживания, механизм регулярных выражений NFA.

Особенности: возможность работы с потоками входных данных и одновременное сопоставление нескольких паттернов. Последний отличается от подхода (pattern1|pattern2|...), на самом деле он соответствует шаблонам одновременно.

Он также использует наборы инструкций SIMD от Intel, такие как SSE4.2, AVX2 и BMI. Краткое изложение дизайна и объяснение работы можно найти здесь . Он также содержит справочное руководство для разработчиков, содержащее множество объяснений, а также вопросы производительности и использования. Небольшая статья об ее использовании в дикой природе.

1 голос
/ 01 января 2010

Кажется, вы знаете начальный и конечный разделители совпадений, которые вы пытаетесь получить, верно? (т. е. [,] или START, END и т. д.) Поэтому имеет ли смысл искать эти разделители при поступлении данных из вашего потока, а затем создавать подстроку между разделителями и выполнять дальнейшую обработку этих?

Я знаю, что это почти то же самое, что и сворачивание вашего собственного, но это будет иметь более конкретную цель и даже сможет обрабатывать его по мере поступления.

Проблема с регулярными выражениями в этом случае заключается в том, что они работают на основе совпадений, поэтому вы можете сравнивать только с количеством вводимых данных. Если у вас есть поток, вам нужно будет прочитать все данные, чтобы получить все совпадения (проблема с ограничением пространства / времени), попытаться сопоставить его с персонажем в занесенное время (довольно бесполезно), сопоставить по частям (опять же, что-то там можно легко пропустить) или создать интересующие строки, которые, если они соответствуют вашим критериям, могут быть отправлены в другое место для дальнейшей обработки.

0 голосов
/ 18 февраля 2016

Вы можете добавить дополнительный метод в StreamReader (для этой цели можно использовать исходный код, например, Mono):

    private StringBuilder lineBuilder;
    public int RegexBufferSize
    {
        set { lastRegexMatchedLength = value; }
        get { return lastRegexMatchedLength; }
    }
    private int lastRegexMatchedLength = 0;

    public virtual string ReadRegex(Regex regex)
    {
        if (base_stream == null)
            throw new ObjectDisposedException("StreamReader", "Cannot read from a closed RegexStreamReader");

        if (pos >= decoded_count && ReadBuffer() == 0)
            return null; // EOF Reached

        if (lineBuilder == null)
            lineBuilder = new StringBuilder();
        else
            lineBuilder.Length = 0;

        lineBuilder.Append(decoded_buffer, pos, decoded_count - pos);
        int bytesRead = ReadBuffer();

        bool dataTested = false;
        while (bytesRead > 0)
        {
            var lineBuilderStartLen = lineBuilder.Length;
            dataTested = false;
            lineBuilder.Append(decoded_buffer, 0, bytesRead);

            if (lineBuilder.Length >= lastRegexMatchedLength)
            {
                var currentBuf = lineBuilder.ToString();
                var match = regex.Match(currentBuf, 0, currentBuf.Length);
                if (match.Success)
                {
                    var offset = match.Index + match.Length;
                    pos = 0;
                    decoded_count = lineBuilder.Length - offset;
                    ensureMinDecodedBufLen(decoded_count);
                    lineBuilder.CopyTo(offset, decoded_buffer, 0, decoded_count);
                    var matchedString = currentBuf.Substring(match.Index, match.Length);
                    return matchedString;
                }
                else
                {
                    lastRegexMatchedLength *= (int) 1.1; // allow for more space before attempting to match
                    dataTested = true;
                }
            }

            bytesRead = ReadBuffer();
        }

        // EOF reached

        if (!dataTested)
        {
            var currentBuf = lineBuilder.ToString();
            var match = regex.Match(currentBuf, 0, currentBuf.Length);
            if (match.Success)
            {
                var offset = match.Index + match.Length;
                pos = 0;
                decoded_count = lineBuilder.Length - offset;
                ensureMinDecodedBufLen(decoded_count);
                lineBuilder.CopyTo(offset, decoded_buffer, 0, decoded_count);
                var matchedString = currentBuf.Substring(match.Index, match.Length);
                return matchedString;

            }
        }
        pos = decoded_count;

        return null;
    }

В приведенном выше методе используются следующие переменные:

  1. decoded_buffer: буфер символов, который содержит / будет содержать прочитанные данные
  2. pos: смещение в массиве, содержащем необработанные данные
  3. decoded_count: последний элемент в буфере, содержащий прочитанные данные
  4. RegexBufferSize: минимальный размер ввода регулярного выражения до того, как произойдет какое-либо совпадение.

Метод ReadBuffer () должен читать данные из потока. Метод sureMinDecodedBufLen () должен убедиться, что decoded_buffer достаточно велик.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...