C#模式搜索

时间:2011-07-02 22:35:57

标签: c#

我有一组十六进制数字,我想搜索一个文件,每次匹配时,它会存储文本文件中模式中找到的最后一个字节的最后一个偏移地址。

所以例如,模式是'04 00 03 00 00 00 04 00 04 00 00 00 08 00'然后每次在文件中找到它然后模式中的最后一个字节'00'的偏移是读入并存储在数组中。

对此有任何帮助,这让我疯了好几个月了。

谢谢


谢谢Svick确实有效......它返回了我需要找到的所有匹配项。

然而,由于我添加了一些我的代码,它在第一场比赛中停止并且没有循环...有人请指出什么是错的以及为什么它在第一场比赛中停止< / p>

非常感谢

3 个答案:

答案 0 :(得分:3)

我假设您将模式作为字符串。你应该做的是将它转换为一个字节数组。

如果要搜索的文件相对较小且效率不是很重要,您可以通过从文件中的每个字节开始搜索并逐字节地比较模式来完成。如果遍历整个模式并且所有字节都匹配,则将位置存储在结果数组中。在代码中,它会是这样的:

byte[] pattern = …;
byte[] file = …;

var result = Enumerable.Range(0, file.Length - pattern.Length + 1)
                       .Where(i => pattern.Select((b, j) => new { j, b })
                                          .All(p => file[i + p.j] == p.b))
                       .Select(i => i + pattern.Length - 1);

此解决方案的时间复杂度为O(HN),其中H是文件的长度(“haystack”),N是模式的长度(“needle”)。

如果文件很大或者你需要快速,你应该使用KMP algorithm,其复杂度为O(H + N)。

答案 1 :(得分:2)

好像你有几个步骤:

  1. 从文本文件中读取模式(针)
  2. 将模式从十六进制ASCII字符转换为字节数组
  3. 将二进制文件(haystack)读入内存
  4. 在大海捞针中寻找针头
  5. 如果整个干草堆立刻适合记忆,这很简单。 std::findmemcmp可以处理第4步(oops,那些是C ++)C#字符串可以包含NUL字节,所以你应该只能使用字符串IndexOf函数这里。您将获得模式开头的偏移量,只需添加模式的长度(减1)即可获得结束字节的偏移量。或者只使用几个for循环自己编写搜索。请参阅@ svick对步骤#4的讨论的优秀答案。

    要处理较大的haystack文件,一次处理一个块,请确保按照模式的长度减1重叠,否则可能会错过跨越块边界的匹配。

答案 2 :(得分:2)

我的想法是使用队列来“寻找”输入。

static void Main(string[] args)
{
    byte[] pattern = new byte[] { 3, 2, 1 };
    byte[] data = new byte[] { 1, 2, 3, 4, 5, 4, 3, 2, 1, 2, 3, 4, 5, 4, 3, 2, 1 };
    foreach (long offset in FindPattern(pattern, data))
    {
        Console.WriteLine(offset);
    }

    Console.ReadLine();
}

public static IEnumerable<long> FindPattern(byte[] pattern, byte[] data)
{
    Queue<byte> queue = new Queue<byte>(pattern.Length);
    using (MemoryStream input = new MemoryStream(data))
    using (BinaryReader reader = new BinaryReader(input))
    {
        byte[] buffer = new byte[1];
        while (1 == reader.Read(buffer, 0, 1))
        {
            if (queue.Count == pattern.Length)
            {
                queue.Dequeue();
            }

            queue.Enqueue(buffer[0]);
            if (Matches(queue, pattern))
            {
                // The input is positioned after the last read byte, which
                // completed the pattern.
                yield return input.Position;
            }
        }
    }
}

private static bool Matches(Queue<byte> data, byte[] pattern)
{
    return data.SequenceEqual(pattern);
}

我确定使用自定义“队列”,它比SequenceEquals()执行更好的比较,但你明白了。