2009-01-02 89 views
171

你是怎么做到的?给定一个字节数组:C中的数组切片#

byte[] foo = new byte[4096]; 

我该如何得到数组的前x个字节作为单独的数组? (具体来说,我需要它作为IEnumerable<byte>

这是与Socket s一起工作。我想最简单的方法是数组切片,类似于Perl的语法:

@bar = @foo[0..40]; 

这将返回第41个元素融入@bar阵列。 C#中是否存在一些我只是缺少的东西,或者是否还有其他一些我应该做的事情?

对我来说LINQ是一个选项(.NET 3.5),如果有帮助的话。

+0

数组切片是C#7.2的一个建议https://github.com/dotnet/csharplang/issues/185 – Mark 2017-03-24 08:47:39

回答

151

数组是可枚举的,所以你的foo已经是一个IEnumerable<byte>本身。 只需使用LINQ序列方法,如Take()得到你想要出它是什么(不要忘了包括Linq命名空间using System.Linq;):

byte[] foo = new byte[4096]; 

var bar = foo.Take(41); 

如果你真的需要从任何IEnumerable<byte>值的数组,你可以为此,请使用ToArray()方法。这似乎并非如此。

+3

如果我们要复制到另一个数组,只需使用Array.Copy静态方法。不过,我认为其他答案已经正确解释了意图,不需要另外一个数组,只需要一个超过前41个字节的IEnumberable 。 – AnthonyWJones 2009-01-02 11:09:47

+2

请注意,只有单维和锯齿状数组是可枚举的,而多维数组则不是。 – Abel 2010-04-08 09:11:19

94

您可以使用数组CopyTo()方法。

或者使用LINQ您可以使用Skip()Take() ...

byte[] arr = {1, 2, 3, 4, 5, 6, 7, 8}; 
var subset = arr.Skip(2).Take(2); 
+0

+1是一个好主意,但我需要使用返回的数组作为另一个函数的输入,这使得CopyTo需要一个临时变量。我会等待其他答案。 – 2009-01-02 10:53:04

+1

我对LINQ还不熟悉,也许这是进一步的证据,我真的应该。 – 2009-01-02 10:55:56

+6

这种方法至少比Array.Copy慢50倍。这在许多情况下都不是问题,但是在循环中进行阵列切片时,性能下降非常明显。 – Valentin 2010-03-04 08:48:08

1

我不认为C#支持范围语义。你可以写,虽然扩展方法,如:

public static IEnumerator<Byte> Range(this byte[] array, int start, int end); 

但是,像其他人所说的,如果你不需要设置一个起始指数则Take是你所需要的。

11

如果你想IEnumerable<byte>,那么就

IEnumerable<byte> data = foo.Take(x); 
4

您可以使用以扩展方法

var array = new byte[] {1, 2, 3, 4}; 
var firstTwoItems = array.Take(2); 
7

您可以在原始数组(即IList)周围使用包装,就像在这个(未经测试的)代码段中一样。

public class SubList<T> : IList<T> 
{ 
    #region Fields 

private readonly int startIndex; 
private readonly int endIndex; 
private readonly int count; 
private readonly IList<T> source; 

#endregion 

public SubList(IList<T> source, int startIndex, int count) 
{ 
    this.source = source; 
    this.startIndex = startIndex; 
    this.count = count; 
    this.endIndex = this.startIndex + this.count - 1; 
} 

#region IList<T> Members 

public int IndexOf(T item) 
{ 
    if (item != null) 
    { 
     for (int i = this.startIndex; i <= this.endIndex; i++) 
     { 
      if (item.Equals(this.source[i])) 
       return i; 
     } 
    } 
    else 
    { 
     for (int i = this.startIndex; i <= this.endIndex; i++) 
     { 
      if (this.source[i] == null) 
       return i; 
     } 
    } 
    return -1; 
} 

public void Insert(int index, T item) 
{ 
    throw new NotSupportedException(); 
} 

public void RemoveAt(int index) 
{ 
    throw new NotSupportedException(); 
} 

public T this[int index] 
{ 
    get 
    { 
     if (index >= 0 && index < this.count) 
      return this.source[index + this.startIndex]; 
     else 
      throw new IndexOutOfRangeException("index"); 
    } 
    set 
    { 
     if (index >= 0 && index < this.count) 
      this.source[index + this.startIndex] = value; 
     else 
      throw new IndexOutOfRangeException("index"); 
    } 
} 

#endregion 

#region ICollection<T> Members 

public void Add(T item) 
{ 
    throw new NotSupportedException(); 
} 

public void Clear() 
{ 
    throw new NotSupportedException(); 
} 

public bool Contains(T item) 
{ 
    return this.IndexOf(item) >= 0; 
} 

public void CopyTo(T[] array, int arrayIndex) 
{ 
    for (int i=0; i<this.count; i++) 
    { 
     array[arrayIndex + i] = this.source[i + this.startIndex]; 
    } 
} 

public int Count 
{ 
    get { return this.count; } 
} 

public bool IsReadOnly 
{ 
    get { return true; } 
} 

public bool Remove(T item) 
{ 
    throw new NotSupportedException(); 
} 

#endregion 

#region IEnumerable<T> Members 

public IEnumerator<T> GetEnumerator() 
{ 
    for (int i = this.startIndex; i < this.endIndex; i++) 
    { 
     yield return this.source[i]; 
    } 
} 

#endregion 

#region IEnumerable Members 

IEnumerator IEnumerable.GetEnumerator() 
{ 
    return GetEnumerator(); 
} 

#endregion 

}

46
static byte[] SliceMe(byte[] source, int length) 
{ 
    byte[] destfoo = new byte[length]; 
    Array.Copy(source, 0, destfoo, 0, length); 
    return destfoo; 
} 

//

var myslice = SliceMe(sourcearray,41); 
164

你可以使用ArraySegment<T>。这是非常轻的重量,因为它不复制阵列:

string[] a = { "one", "two", "three", "four", "five" }; 
var segment = new ArraySegment<string>(a, 1, 2); 
15

另一种可能性,我没有看到在这里提到:缓冲区。BlockCopy()比Array.Copy()稍快,并且它具有能够从基元数组(即short [])实时转换为字节数组的附加好处,它可以是当你有需要通过套接字传输的数字数组时,方便使用。

6
byte[] foo = new byte[4096]; 

byte[] bar = foo.Take(40).ToArray(); 
13

这里有一个返回片作为新阵列的简单扩展方法:

public static T[] Slice<T>(this T[] arr, uint indexFrom, uint indexTo) { 
    if (indexFrom > indexTo) { 
     throw new ArgumentOutOfRangeException("indexFrom is bigger than indexTo!"); 
    } 

    uint length = indexTo - indexFrom; 
    T[] result = new T[length]; 
    Array.Copy(arr, indexFrom, result, 0, length); 

    return result; 
} 

然后你可以使用它作为:

byte[] slice = foo.Slice(0, 40); 
3

这可能是一个解决方案:

var result = foo.Slice(40, int.MaxValue); 

然后结果IEnumerable的< IEnumerable的<字节>>与第一的IEnumerable <字节>包含第一40个字节FOO的,以及第二的IEnumerable <字节>保持静止。

我写了一个包装类,整个迭代很懒,希望它可以帮助:

public static class CollectionSlicer 
{ 
    public static IEnumerable<IEnumerable<T>> Slice<T>(this IEnumerable<T> source, params int[] steps) 
    { 
     if (!steps.Any(step => step != 0)) 
     { 
      throw new InvalidOperationException("Can't slice a collection with step length 0."); 
     } 
     return new Slicer<T>(source.GetEnumerator(), steps).Slice(); 
    } 
} 

public sealed class Slicer<T> 
{ 
    public Slicer(IEnumerator<T> iterator, int[] steps) 
    { 
     _iterator = iterator; 
     _steps = steps; 
     _index = 0; 
     _currentStep = 0; 
     _isHasNext = true; 
    } 

    public int Index 
    { 
     get { return _index; } 
    } 

    public IEnumerable<IEnumerable<T>> Slice() 
    { 
     var length = _steps.Length; 
     var index = 1; 
     var step = 0; 

     for (var i = 0; _isHasNext; ++i) 
     { 
      if (i < length) 
      { 
       step = _steps[i]; 
       _currentStep = step - 1; 
      } 

      while (_index < index && _isHasNext) 
      { 
       _isHasNext = MoveNext(); 
      } 

      if (_isHasNext) 
      { 
       yield return SliceInternal(); 
       index += step; 
      } 
     } 
    } 

    private IEnumerable<T> SliceInternal() 
    { 
     if (_currentStep == -1) yield break; 
     yield return _iterator.Current; 

     for (var count = 0; count < _currentStep && _isHasNext; ++count) 
     { 
      _isHasNext = MoveNext(); 

      if (_isHasNext) 
      { 
       yield return _iterator.Current; 
      } 
     } 
    } 

    private bool MoveNext() 
    { 
     ++_index; 
     return _iterator.MoveNext(); 
    } 

    private readonly IEnumerator<T> _iterator; 
    private readonly int[] _steps; 
    private volatile bool _isHasNext; 
    private volatile int _currentStep; 
    private volatile int _index; 
} 
1

下面是一个使用一个通用的,并且表现得像PHP函数array_slice的扩展功能。负偏移和长度是允许的。

public static class Extensions 
{ 
    public static T[] Slice<T>(this T[] arr, int offset, int length) 
    { 
     int start, end; 

     // Determine start index, handling negative offset. 
     if (offset < 0) 
      start = arr.Length + offset; 
     else 
      start = offset; 

     // Clamp start index to the bounds of the input array. 
     if (start < 0) 
      start = 0; 
     else if (start > arr.Length) 
      start = arr.Length; 

     // Determine end index, handling negative length. 
     if (length < 0) 
      end = arr.Length + length; 
     else 
      end = start + length; 

     // Clamp end index to the bounds of the input array. 
     if (end < 0) 
      end = 0; 
     if (end > arr.Length) 
      end = arr.Length; 

     // Get the array slice. 
     int len = end - start; 
     T[] result = new T[len]; 
     for (int i = 0; i < len; i++) 
     { 
      result[i] = arr[start + i]; 
     } 
     return result; 
    } 
} 
5

如果你不希望添加LINQ或其他扩展只是做:

float[] subArray = new List<float>(myArray).GetRange(0, 8).ToArray();