2009-01-02 93 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();