【问题标题】:Locating SubArray in ByteArray在 ByteArray 中定位子数组
【发布时间】:2020-05-19 11:54:13
【问题描述】:

摘要

从块中读取文件中的字节(在 128 - 1024 之间没有特定大小,尚未决定),我想搜索缓冲区以查看它是否包含另一个字节数组的签名(模式),如果它在缓冲区的最后找到一些模式,它应该从文件中读取接下来的几个字节,看看它是否找到了匹配项

我的尝试

public static bool Contains(byte[] buffer, byte[] signiture, FileStream file)
{
    for (var i = buffer.Length - 1; i >= signiture.Length - 1; i--) //move backwards through array stop if < signature
    {
        var found = true; //set found to true at start
        for (var j = signiture.Length - 1; j >= 0 && found; j--) //loop backwards throughsignature
        {
            found = buffer[i - (signiture.Length - 1 - j)] == signiture[j];// compare signature's element with corresponding element of buffer
        }
        if (found)
            return true; //if signature is found return true
    }


    //checking end of buffer for partial signiture
    for (var x = signiture.Length - 1; x >= 1; x--)
    {
        if (buffer.Skip(buffer.Length - x).Take(x).SequenceEqual(signiture.Skip(0).Take(x))) //check if partial is equal to partial signiture
        {
            byte[] nextBytes = new byte[signiture.Length - x];
            file.Read(nextBytes, 0, signiture.Length - x); //read next needed bytes from file
            if (!signiture.Skip(0).Take(x).ToArray().Concat(nextBytes).SequenceEqual(signiture))
                return false; //return false if not a match
            return true; //return true if a match
        }
    }
    return false; //if not found return false
}

这可行,但有人告诉我 linq 很慢,我应该使用 Array.IndexOf()。我已经尝试过了,但无法弄清楚如何实现它

【问题讨论】:

  • Linq 会占用大量内存,这会使某些查询运行缓慢。如果您只是在一个小数组上运行数据,那么在 linq 中可能会运行得更快。

标签: c# search buffer streamreader indexof


【解决方案1】:

您可以使用Span&lt;T&gt;, AsSpanMemoryExtensions.SequenceEqual。后者不是 LINQ;它经过优化,特别是对于字节数组。它展开循环并使用不安全代码来执行memcmp

如果您没有使用默认包含这些类型/方法的框架(.Netcore2.1+、.Netstandard 2.1),您可以添加System.Memory nuget 包。 SequenceEqual 的实现有点不同(所谓的“慢版本”),但它仍然比使用 LINQ 的 SequenceEqual 更快。

请注意,您还需要检查FileStream.Read 的返回值。

public static bool Contains(byte[] buffer, byte[] signiture, FileStream file)
{
    var sigSpan = signiture.AsSpan();

    //move backwards through buffer and check if signature found
    for (var i = buffer.Length - signiture.Length; i >= 0; i--)
    { 
        if (buffer.AsSpan(i, signiture.Length).SequenceEqual(sigSpan))
            return true;
    }

    for (var x = signiture.Length - 1; x >= 1; x--)
    {
        var sig = sigSpan.Slice(0, x);
        if (buffer.AsSpan(buffer.Length - x).SequenceEqual(sig)) //check if partial is equal to partial signiture
        {
            var sigLen = signiture.Length;
            byte[] nextBytes = ArrayPool<byte>.Shared.Rent(sigLen - x);

            // need to store number of bytes read
            var read = file.Read(nextBytes, 0, sigLen - x); //read next needed bytes from file
            var next = nextBytes.AsSpan(0, read);

            // don't need to concat with signature, because obviously signature is going to 
            // start with signature.Skip(0).Take(...)
            // just test that the number of bytes we read, plus the number we will skip equals
            // the actual length, then check the remainder
            var result = (read + x == signiture.Length 
                       && signiture.AsSpan(x).SequenceEqual(next));

            ArrayPool<byte>.Shared.Return(nextBytes);
            return result;
        }
    }

    return false; //if not found return false

}

【讨论】:

  • 谢谢,这似乎加快了他们的速度,谢谢。刚刚进行了一些基准测试,似乎更小的缓冲区大小更好graph
  • 显然更小的缓冲区会更好。否则,您仍在进行指数比较。这个版本的优点是它避免了所有的数组分配。您的版本构造了一堆额外的数组。这个版本的总数是两个(传递给函数的那个​​)我做了一个编辑,使用ArrayPool作为你的nextBytes
  • 这样会加快速度吗?
猜你喜欢
  • 1970-01-01
  • 2011-11-13
  • 2014-12-27
  • 2013-04-07
  • 1970-01-01
  • 2019-05-04
  • 1970-01-01
  • 1970-01-01
  • 2021-01-01
相关资源
最近更新 更多