Применить регулярное выражение в потоке?
Я ищу быстрый и безопасный способ применения регулярных выражений в потоках.
Я нашел несколько примеров в Интернете, говорящих о преобразовании каждого буфера в String, а затем применил Regex
в строке.
Этот подход имеет две проблемы:
- Производительность: преобразование в строки и GC'ing строк - это трата времени и процессора, и этого можно избежать, если бы был более естественный способ применения
Regex
в потоках.
- Pure
Regex
support: Regex
pattern иногда может совпадать только при объединении двух буферов вместе (буфер 1 заканчивается первой частью совпадения, а буфер 2 начинается со второй части совпадения). Метод convert-to-string не может обрабатывать этот тип соответствия изначально, я должен предоставить больше информации, такой как максимальная длина, с которой может сравниться шаблон, это не поддерживает знаки + и * regex вообще и никогда не будет поддерживать (неограниченное совпадение длина).
Итак, способ конвертирования в строку не выполняется быстро и не поддерживает полностью Regex
.
Есть ли способ/библиотека, который можно использовать для применения Regex
в потоках без преобразования в строки и с полной поддержкой Regex?
Ответы
Ответ 1
Недавно Intel открыла библиотеку hyperscan под лицензией BSD. Это высокопроизводительный механизм обратного отслеживания на основе NFA.
Особенности: возможность работы с потоками входных данных и одновременное сопоставление нескольких шаблонов. Последний отличается от подхода (pattern1|pattern2|...)
, он фактически сопоставляет шаблоны одновременно.
В нем также используются наборы инструкций Intel SIMD, такие как SSE4.2, AVX2 и BMI.
Резюме проекта и объяснения работы можно найти здесь здесь.
Он также обладает отличным разработчиком справочным руководством с большим количеством объяснений, а также соображениями производительности и использования.
Маленькая статья об использовании этого в дикой природе.
Ответ 2
Возможно, эта статья может помочь? Хотя я полагаю, что это могут быть "вещи в Интернете", которые вы обнаружили, которые не помогли.
Создание потока регулярных выражений с помощью .NET Framework
Ответ 3
Кажется, что вы знаете начальные и конечные разделители совпадений, которые вы пытаетесь получить, правильно? (т.е. [,] или START, END и т.д.). Было бы разумно искать эти разделители, когда поступают данные из вашего потока, а затем создает подстроку между разделителями и выполняет дальнейшую обработку на них?
Я знаю, что это почти то же самое, что и ваш собственный, но он будет иметь более конкретную цель и даже сможет обрабатывать его, как он есть.
Проблема с регулярными выражениями в этом случае заключается в том, что они работают на основе совпадений, поэтому вы можете сопоставлять только количество вводимых вами данных. Если у вас есть поток, вам нужно будет прочитать все данные, чтобы получить все совпадения (проблема ограничения пространства/времени), попытайтесь совместить с персонажем в то время, когда он был принесен (довольно бесполезный), совпадение в кусках (опять же, что-то можно легко пропустить там) или создать интересующие строки, которые, если они соответствуют вашим критериям, могут быть отправлены в другое место для дальнейшей обработки.
Ответ 4
Вы можете добавить дополнительный метод для 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;
}
В приведенном выше методе используются следующие вары:
- decoded_buffer: буфер char, содержащий/будет содержать прочитанные данные
- pos: смещение внутри массива, содержащего необработанные данные
- decoded_count: последний элемент в буфере, содержащий считанные данные
- RegexBufferSize: минимальный размер ввода регулярного выражения до того, как произойдет соответствующее сопоставление.
Метод ReadBuffer() должен читать данные из потока.
Метод makeMinDecodedBufLen() должен убедиться, что decoded_buffer достаточно велик.
При вызове метода передайте регулярное выражение, которое необходимо сопоставить.