在Stream上应用正则表达式?

时间:2009-12-25 23:26:21

标签: c# .net regex streaming

我正在寻找快速安全的方法在Streams上应用正则表达式。

我在互联网上找到了一些关于将每个缓冲区转换为String然后在字符串上应用Regex的例子。

这种方法有两个问题:

  • 性能:转换为字符串和GC字符串是浪费时间和CPU,如果有一种更原生的方式在Streams上应用Regex,那么肯定可以避免。
  • Regex支持:Regex模式有时只能将两个缓冲区组合在一起(缓冲区1以匹配的第一部分结束,缓冲区2以匹配的第二部分开始) 。转换为字符串的方式无法原生地处理这种类型的匹配,我必须提供更多信息,例如模式可以匹配的最大长度,这根本不支持+和*正则表达式标志,并且永远不会支持(无限制匹配)长度)。

因此,转换为字符串的方式并不快,并且不完全支持Regex

是否有任何方法/库可用于在Streams上应用Regex而无需转换为字符串并具有完整的Regex支持?

3 个答案:

答案 0 :(得分:5)

英特尔最近在BSD许可下开源hyperscan库。这是一款基于NFA的高性能非回溯正则表达式引擎。

功能:能够处理输入数据流和同时进行多种模式匹配。最后一个与(pattern1|pattern2|...)方法不同,它实际上是同时匹配模式。

它还利用了英特尔的SIMD指令集,如SSE4.2,AVX2和BMI。 可以找到here的设计和工作说明摘要。 它还有很棒的开发人员reference guide,有很多解释以及性能和使用注意事项。 小article关于在野外使用它(俄语)。

答案 1 :(得分:1)

您似乎想知道您想要获得的比赛的开始和结束分隔符,对吗? (即[,]或START,END等)因此,当您的流中的数据进入,然后在分隔符之间创建子字符串并对其进行进一步处理时,搜索这些分隔符是否有意义?

我知道它与滚动自己的东西几乎相同,但它将具有更具体的目的,甚至可以在它进入时进行处理。

此实例中正则表达式的问题在于它们基于匹配工作,因此您只能匹配您拥有的输入量。如果你有一个流,你必须读入所有数据以获得所有匹配(空间/时间约束问题),尝试在引入时匹配角色(相当无用),匹配块(再次,那些东西很容易被错过)或产生感兴趣的字符串,如果符合你的标准,可以从其他地方运出进行进一步处理。

答案 2 :(得分:-1)

您可以向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. decoding_buffer:包含/将包含读取数据的char缓冲区
  2. pos:包含未处理数据的数组中的偏移量
  3. decoding_count:缓冲区中包含读取数据的最后一个元素
  4. RegexBufferSize:发生任何匹配之前正则表达式输入的最小大小。
  5. ReadBuffer()方法需要从流中读取数据。 方法ensureMinDecodedBufLen()需要确保decode_buffer足够大。

    调用方法时,传递需要匹配的正则表达式。