c#中的数组片

你是怎么做到的?给定一个字节数组:

byte[] foo = new byte[4096];

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

这是用于处理Sockets。我认为最简单的方法是数组切片,类似于perl语法:

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

将前41个元素返回到@bar数组中。c#中是否有我遗漏的东西,或者有其他我应该做的事情?

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

372252 次浏览

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

或者在LINQ中,你可以使用Skip()Take()

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

我不认为c#支持范围语义。你也可以写一个扩展方法,比如:

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

但就像其他人说的,如果你不需要设置开始索引,那么Take就是你所需要的。

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

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

您可以使用Take扩展方法

var array = new byte[] {1, 2, 3, 4};
var firstTwoItems = array.Take(2);
数组是可枚举的,所以你的foo本身已经是一个IEnumerable<byte>。 只需使用像Take()这样的LINQ序列方法就可以得到你想要的结果(别忘了用using System.Linq;包含Linq命名空间):

byte[] foo = new byte[4096];


var bar = foo.Take(41);

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

您可以对原始数组(即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
}
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);

你可以使用ArraySegment<T>。它非常轻量级,因为它不复制数组:

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

我在这里没有看到的另一种可能性是:Buffer.BlockCopy()比array . copy()稍微快一些,而且它还有一个额外的好处,就是能够实时地将原语数组(比如short[])转换为字节数组,这在需要通过socket传输数字数组时非常方便。

byte[] foo = new byte[4096];


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

下面是一个简单的扩展方法,它返回一个slice作为一个新数组:

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);

这可能是一个解决方案:

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

然后结果是一个IEnumerable<IEnumerable<字节> >,其中第一个IEnumerable<字节>包含喷火的前40个字节,第二个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;
}

下面是一个使用泛型的扩展函数,其行为类似于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;
}
}

如果你不想添加LINQ或其他扩展,只需要:

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

对于字节数组,System.Buffer.BlockCopy将提供最好的性能。

c# 7.2中,可以使用Span<T>。新的System.Memory系统的好处是它不需要复制数据。

你需要的方法是Slice:

Span<byte> slice = foo.Slice(0, 40);

现在很多方法都支持SpanIReadOnlySpan,所以使用这个新类型非常简单。

请注意,在编写时,Span<T>类型还没有在最新的。net版本(4.7.1)中定义,因此要使用它,您需要从NuGet安装系统。内存包

从c# 8.0/开始。Net Core 3.0

数组切片将被支持,同时添加了新类型IndexRange

< p > 范围结构文档 < br > 索引结构文档 < / p >
Index i1 = 3;  // number 3 from beginning
Index i2 = ^4; // number 4 from end
int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Console.WriteLine($"{a[i1]}, {a[i2]}"); // "3, 6"


var slice = a[i1..i2]; // { 3, 4, 5 }

以上代码示例取自c# 8.0 博客

注意,^前缀表示从数组的结束开始计数。如文档的例子中所示

var words = new string[]
{
// index from start    index from end
"The",      // 0                   ^9
"quick",    // 1                   ^8
"brown",    // 2                   ^7
"fox",      // 3                   ^6
"jumped",   // 4                   ^5
"over",     // 5                   ^4
"the",      // 6                   ^3
"lazy",     // 7                   ^2
"dog"       // 8                   ^1
};              // 9 (or words.Length) ^0

RangeIndex也适用于切片数组之外,例如循环

Range range = 1..4;
foreach (var name in names[range])

将循环1到4项


< p > 注意,在写这个答案的时候,c# 8.0还没有正式发布 < br > c# 8。. x和。net Core 3。 . x现在可在Visual Studio 2019及以后使用

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace data_seniens
{
class Program
{
static void Main(string[] args)
{
//new list
float [] x=new float[]{11.25f,18.0f,20.0f,10.75f,9.50f, 11.25f, 18.0f, 20.0f, 10.75f, 9.50f };


//variable
float eat_sleep_area=x[1]+x[3];
//print
foreach (var VARIABLE in x)
{
if (VARIABLE < x[7])
{
Console.WriteLine(VARIABLE);
}
}






//keep app run
Console.ReadLine();
}
}
}

c# 8现在(自2019年起)支持范围,这让你更容易实现Slice(类似于JS语法):

var array = new int[] { 1, 2, 3, 4, 5 };
var slice1 = array[2..^3];    // array[new Range(2, new Index(3, fromEnd: true))]
var slice2 = array[..^3];     // array[Range.EndAt(new Index(3, fromEnd: true))]
var slice3 = array[2..];      // array[Range.StartAt(2)]
var slice4 = array[..];       // array[Range.All]

你可以使用范围来代替众所周知的LINQ函数:跳过()带()Count ()