【问题标题】:How to split a byte array如何拆分字节数组
【发布时间】:2010-09-06 10:54:33
【问题描述】:

我在内存中有一个字节数组,从文件中读取。我想在某个点(索引)拆分字节数组,而不必只创建一个新的字节数组并一次复制每个字节,从而增加操作的内存占用量。我想要的是这样的:

byte[] largeBytes = [1,2,3,4,5,6,7,8,9];  
byte[] smallPortion;  
smallPortion = split(largeBytes, 3);  

smallPortion 等于 1,2,3,4
largeBytes 等于 5,6,7,8,9

【问题讨论】:

    标签: c# arrays split


    【解决方案1】:

    在带有 Linq 的 C# 中,您可以这样做:

    smallPortion = largeBytes.Take(4).ToArray();
    largeBytes = largeBytes.Skip(4).Take(5).ToArray();
    

    ;)

    【讨论】:

    • OP 想知道如何做到这一点不必 [...] 创建一个新的字节数组并一次复制每个字节 但这正是您的 LINQ代码可以。两次。
    • ..这帮助我解决了ArraySegment<T>..没有解决的问题。
    • @Christian,为了避免使用额外的内存,只需删除两个“.ToArray()”调用。这将返回两个 IEnumerable,其代码行数要少得多。
    • @GerardoGrignoli 是的,但这会给你一个IEnumerable<byte>。虽然迭代该可枚举多次是完全安全(且快速)的,但有两个缺点:a)您仍然不能直接索引到数组的该部分。当然,Skip/Take 的 LINQ 实现利用了数组结构,但他们通过尝试将 IEnumerable 强制转换为 Collection 或 Array 来实现这一点。 b) 一旦您从 API 返回一个 IEnumerable,客户端就不再保证(由类型系统)该 enumerable 可以安全且高效地进行多次迭代。
    【解决方案2】:

    仅供参考。 System.ArraySegment<T> 结构与上面代码中的ArrayView<T> 基本相同。如果您愿意,可以以同样的方式使用这种开箱即用的结构。

    【讨论】:

    • 有趣。可惜我在做那个项目的时候没有看到这个。无论如何感谢您的信息。
    【解决方案3】:

    我会这样做:

    using System;
    using System.Collections;
    using System.Collections.Generic;
    
    class ArrayView<T> : IEnumerable<T>
    {
        private readonly T[] array;
        private readonly int offset, count;
    
        public ArrayView(T[] array, int offset, int count)
        {
            this.array = array;
            this.offset = offset;
            this.count = count;
        }
    
        public int Length
        {
            get { return count; }
        }
    
        public T this[int index]
        {
            get
            {
                if (index < 0 || index >= this.count)
                    throw new IndexOutOfRangeException();
                else
                    return this.array[offset + index];
            }
            set
            {
                if (index < 0 || index >= this.count)
                    throw new IndexOutOfRangeException();
                else
                    this.array[offset + index] = value;
            }
        }
    
        public IEnumerator<T> GetEnumerator()
        {
            for (int i = offset; i < offset + count; i++)
                yield return array[i];
        }
    
        IEnumerator IEnumerable.GetEnumerator()
        {
            IEnumerator<T> enumerator = this.GetEnumerator();
            while (enumerator.MoveNext())
            {
                yield return enumerator.Current;
            }
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            byte[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
            ArrayView<byte> p1 = new ArrayView<byte>(arr, 0, 5);
            ArrayView<byte> p2 = new ArrayView<byte>(arr, 5, 5);
            Console.WriteLine("First array:");
            foreach (byte b in p1)
            {
                Console.Write(b);
            }
            Console.Write("\n");
            Console.WriteLine("Second array:");
            foreach (byte b in p2)
            {
                Console.Write(b);
            }
            Console.ReadKey();
        }
    }
    

    【讨论】:

      【解决方案4】:

      试试这个:

      private IEnumerable<byte[]> ArraySplit(byte[] bArray, int intBufforLengt)
          {
              int bArrayLenght = bArray.Length;
              byte[] bReturn = null;
      
              int i = 0;
              for (; bArrayLenght > (i + 1) * intBufforLengt; i++)
              {
                  bReturn = new byte[intBufforLengt];
                  Array.Copy(bArray, i * intBufforLengt, bReturn, 0, intBufforLengt);
                  yield return bReturn;
              }
      
              int intBufforLeft = bArrayLenght - i * intBufforLengt;
              if (intBufforLeft > 0)
              {
                  bReturn = new byte[intBufforLeft];
                  Array.Copy(bArray, i * intBufforLengt, bReturn, 0, intBufforLeft);
                  yield return bReturn;
              }
          }
      

      【讨论】:

      • 我觉得应该是静态的
      【解决方案5】:

      作为Eren said,您可以使用ArraySegment&lt;T&gt;。下面是一个扩展方法和使用示例:

      public static class ArrayExtensionMethods
      {
          public static ArraySegment<T> GetSegment<T>(this T[] arr, int offset, int? count = null)
          {
              if (count == null) { count = arr.Length - offset; }
              return new ArraySegment<T>(arr, offset, count.Value);
          }
      }
      
      void Main()
      {
          byte[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
          var p1 = arr.GetSegment(0, 5);
          var p2 = arr.GetSegment(5);
          Console.WriteLine("First array:");
          foreach (byte b in p1)
          {
              Console.Write(b);
          }
          Console.Write("\n");
          Console.WriteLine("Second array:");
          foreach (byte b in p2)
          {
              Console.Write(b);
          }
      }
      

      【讨论】:

        【解决方案6】:

        我不确定你的意思:

        我想在某个点(索引)拆分字节数组,而不必只创建一个新的字节数组并一次复制每个字节,从而增加操作的内存占用量。

        在大多数语言中,尤其是 C#,一旦分配了一个数组,就无法改变它的大小。听起来您正在寻找一种方法来更改数组的长度,而这是您无法做到的。您还想以某种方式回收数组第二部分的内存,以创建第二个数组,您也不能这样做。

        总而言之:只需创建一个新数组即可。

        【讨论】:

          【解决方案7】:

          你不能。您可能想要的是保留起点和项目数量;本质上,构建迭代器。如果这是 C++,您可以使用 std::vector&lt;int&gt; 并使用内置的。

          在 C# 中,我会构建一个小型迭代器类,它包含起始索引、计数并实现 IEnumerable&lt;&gt;

          【讨论】:

            【解决方案8】:

            我尝试了不同的算法:

            • Skip().Take() => 最糟糕的,到目前为止
            • Array.Copy
            • ArraySegment
            • 新的 Guid(int, int16, int16 ...)

            最新的是最快的我现在使用这个扩展方法:

                    public static Guid ToGuid(this byte[] byteArray, int offset)
                    {
                        return new Guid(BitConverter.ToInt32(byteArray, offset), BitConverter.ToInt16(byteArray, offset + 4), BitConverter.ToInt16(byteArray, offset + 6), byteArray[offset + 8], byteArray[offset + 9], byteArray[offset + 10], byteArray[offset + 11], byteArray[offset + 12], byteArray[offset + 13], byteArray[offset + 14], byteArray[offset + 15]);
                    }
            

            使用具有 10000000 个 guid 的字节数组:

            Done (Skip().Take()) in 1,156ms (for only 100000 guids :))
            Done (Array.Copy) in 1,219ms
            Done (ToGuid extension) in 994ms
            Done (ArraySegment) in 2,411ms
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-03-23
              • 2012-03-15
              • 1970-01-01
              相关资源
              最近更新 更多