Intereting Posts

Применить регулярное выражение в streamе?

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

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

Этот подход имеет две проблемы:

  • Производительность: преобразование в строки и GC’ing строк – это трата времени и процессора, и этого можно избежать, если существует более привычный способ применения Regex on Streams.
  • Поддержка Pure Regex : шаблон Regex иногда может совпадать только при объединении двух буферов (буфер 1 заканчивается первой частью совпадения, а буфер 2 начинается со второй части совпадения). Метод convert-to-string не может обрабатывать этот тип соответствия изначально, я должен предоставить больше информации, такой как максимальная длина, с которой может сравниться шаблон, это не поддерживает знаки + и * regex вообще и никогда не будет поддерживать (неограниченное совпадение длина).

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

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

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

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

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

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

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

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

    Вы можете добавить дополнительный метод 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 () должен читать данные из streamа. Метод makeMinDecodedBufLen () должен убедиться, что decoded_buffer достаточно велик.

    При вызове метода передайте Regex, с которым нужно сопоставить.