【问题标题】:How to check if bytes array contains another array如何检查字节数组是否包含另一个数组
【发布时间】:2016-05-02 17:07:34
【问题描述】:

我有一个很长的字节数组,例如:

Byte[] bytes = {90, 80, 63, 65, 70 ...};

大约 20-30 Mb(理论上)。有没有一种快速的方法来检查这个数组是否包含另一个数组,例如:

Byte[] small = {63, 80, 75, 77};

首先,我需要按照它们在小数组中定义的顺序查找字节。其次,我需要在另一个数组中找到数组,而不是小数组的任何字节。 谢谢大家的进步。

【问题讨论】:

  • 你能说得更具体点吗?你的意思是确切的顺序还是那些数字,在任何地方的任何顺序?
  • 您是否在寻找 sequence,即这四个字节必须按该顺序出现?如果是这样,这通常被称为substring search(即使它不涉及您正在使用的任何语言的字符串),您应该能够为它查找相当多的算法。
  • 您希望它们以相同的顺序显示吗?如果顺序无关紧要,那么您可能可以对初始数组进行排序,然后对候选者进行二进制搜索。如果顺序很重要,那么有各种算法可以帮助您。在我看来,Aho Corasick 可能是你情况下最快的。
  • 您可以尝试IsSubsetOf 方法,如this answer 中所述。

标签: c# arrays algorithm


【解决方案1】:

你可以使用这个函数,来自this Reddit post

public static bool CheckSequence<T>(IEnumerable<T> containingArray, IEnumerable<T> containedArray)
{
    bool result = false;
    for (int i = 0; i <= containingArray.Count(); i++)
    {
        if (containedArray.SequenceEqual(containingArray.Skip(i).Take(containedArray.Count())))
            result = true;
    }
    return result;
}

喜欢:

var result = CheckSequence(bytes, small);

【讨论】:

    【解决方案2】:

    如果我理解正确,您想说small 是否是bytes 的子序列。您可以通过字节循环找到它。由于处理器缓存,它应该运行得非常快。

      for (int i = 0, index = 0; i < bytes.Length; ++i) 
        if (bytes[i] == small[index]) {
          if (++index >= small.Length) {
            return true; 
          }
        }
      return false;
    

    【讨论】:

    • @PetterHesselberg 你的意思是它没有正确检测子序列还是没有解决顶部令人困惑的问题?
    • 它甚至不能按原样编译;该代码包含一个左大括号和两个右大括号。但这是一个小问题,很容易解决。问题是它会发现误报——如果small 的字节存在于bytes 中,但中间有其他值,您的算法仍会返回true。我确实相信 OP 想要一个连续的字节序列。
    • @PetterHesselberg 感谢您找到错字。这些不是误报,这是子序列的定义。他们的 OP 说“首先,我需要按照它们在小数组中定义的顺序查找字节”,我将其理解为子序列。上面的代码提供了这一点。第二个看不懂希望OP回来澄清。
    【解决方案3】:

    为了提高性能,您需要Boyer-Moore string search algorithm 之类的东西。虽然它是为字符串设计的,但它在字节数组上也应该同样适用,并且比暴力解决方案性能要好得多

    Wikipedia 文章提供了几种实现,包括一种用 Java 编写的,一种用 C 编写的,因此创建 C# 实现应该相当轻松。


    事实证明,将 Wikipedia 文章中的 Boyer-Moore 的 Java 实现翻译成 C#(以及将 char 翻译成 byte)确实很轻松。这里是:

    public static class BoyerMoore
    {
        public static int IndexOf(byte[] haystack, byte[] needle)
        {
            if (needle.Length == 0)
            {
                return 0;
            }
    
            int[] charTable = MakeCharTable(needle);
            int[] offsetTable = MakeOffsetTable(needle);
            for (int i = needle.Length - 1; i < haystack.Length;)
            {
                int j;
                for (j = needle.Length - 1; needle[j] == haystack[i]; --i, --j)
                {
                    if (j == 0)
                    {
                        return i;
                    }
                }
    
                i += Math.Max(offsetTable[needle.Length - 1 - j], charTable[haystack[i]]);
            }
    
            return -1;
        }
    
        private static int[] MakeCharTable(byte[] needle)
        {
            const int ALPHABET_SIZE = 256;
            int[] table = new int[ALPHABET_SIZE];
            for (int i = 0; i < table.Length; ++i)
            {
                table[i] = needle.Length;
            }
    
            for (int i = 0; i < needle.Length - 1; ++i)
            {
                table[needle[i]] = needle.Length - 1 - i;
            }
    
            return table;
        }
    
        private static int[] MakeOffsetTable(byte[] needle)
        {
            int[] table = new int[needle.Length];
            int lastPrefixPosition = needle.Length;
            for (int i = needle.Length - 1; i >= 0; --i)
            {
                if (IsPrefix(needle, i + 1))
                {
                    lastPrefixPosition = i + 1;
                }
    
                table[needle.Length - 1 - i] = lastPrefixPosition - i + needle.Length - 1;
            }
    
            for (int i = 0; i < needle.Length - 1; ++i)
            {
                int slen = SuffixLength(needle, i);
                table[slen] = needle.Length - 1 - i + slen;
            }
    
            return table;
        }
    
        private static bool IsPrefix(byte[] needle, int p)
        {
            for (int i = p, j = 0; i < needle.Length; ++i, ++j)
            {
                if (needle[i] != needle[j])
                {
                    return false;
                }
            }
    
            return true;
        }
    
        private static int SuffixLength(byte[] needle, int p)
        {
            int len = 0;
            for (int i = p, j = needle.Length - 1; i >= 0 && needle[i] == needle[j]; --i, --j)
            {
                len += 1;
            }
    
            return len;
        }
    }
    

    该算法在开始时会花费一些线性时间来构建其表;从那时起,它的速度非常快。

    【讨论】:

      【解决方案4】:

      使用这个:

      bool isSubset = !t2.Except(t1).Any();
      

      来自@Farhad Jabiyev 的链接: Check whether an array is a subset of another

      【讨论】:

      • OP 不会将数组视为 sets 而是视为 sequences
      • 解决不了问题。它不会按照小数组中定义字节的顺序在大数组中定位小数组。
      【解决方案5】:
      static int search(byte[] haystack, byte[] needle)
      {
          for (int i = 0; i <= haystack.Length - needle.Length; i++)
          {
              if (match(haystack, needle, i))
              {
                  return i;
              }
          }
          return -1;
      }
      
      static bool match(byte[] haystack, byte[] needle, int start)
      {
          if (needle.Length + start > haystack.Length)
          {
              return false;
          }
          else
          {
              for (int i = 0; i < needle.Length; i++)
              {
                  if (needle[i] != haystack[i + start])
                  {
                      return false;
                  }
              }
              return true;
          }
      }
      

      【讨论】:

        【解决方案6】:

        如果您有数百万字节的元素,我建议:

        1. 对字节排序
        2. foreach in small 如果找不到元素返回 false

        这样

        bytes.Sort(); // only need to do this once.
        bool smallContained = ContainsAll(bytes, small);
        

        static bool ContainsAll(int[] src, int [] subset)
        { 
            foreach(var i in subset)
               if (src.BinarySearch(i) < 0)
                       return false;
            return true;
        }
        

        【讨论】:

        • 我需要按照它们在小数组中定义的顺序查找字节。
        猜你喜欢
        • 2017-05-30
        • 2023-03-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-10-28
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多