你是怎么做到的?给定一个字节数组:
byte[] foo = new byte[4096];
我如何得到数组的前x字节作为一个单独的数组?(具体来说,我需要它作为一个IEnumerable<byte>)
这是用于使用Sockets的。我认为最简单的方法是数组切片,类似于perl语法:
@bar = @foo[0..40];
将前41个元素返回到@bar数组中。c#中是否有我遗漏的东西,或者有其他我应该做的事情?
LINQ对我来说是一个选择。NET 3.5),如果有帮助的话。
你是怎么做到的?给定一个字节数组:
byte[] foo = new byte[4096];
我如何得到数组的前x字节作为一个单独的数组?(具体来说,我需要它作为一个IEnumerable<byte>)
这是用于使用Sockets的。我认为最简单的方法是数组切片,类似于perl语法:
@bar = @foo[0..40];
将前41个元素返回到@bar数组中。c#中是否有我遗漏的东西,或者有其他我应该做的事情?
LINQ对我来说是一个选择。NET 3.5),如果有帮助的话。
当前回答
byte[] foo = new byte[4096];
byte[] bar = foo.Take(40).ToArray();
其他回答
你可以使用ArraySegment<T>。它非常轻量级,因为它不复制数组:
string[] a = { "one", "two", "three", "four", "five" };
var segment = new ArraySegment<string>( a, 1, 2 );
如果你想要IEnumerable<byte>,那么只是
IEnumerable<byte> data = foo.Take(x);
这可能是一个解决方案:
var result = foo.Slice(40, int.MaxValue);
然后结果是一个IEnumerable< IEnumerable<字节>>,其中第一个IEnumerable<字节>包含foo的前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;
}
}
从c# 8.0/开始。Net Core 3.0
数组切片将被支持,同时添加了新的类型Index和Range。
范围结构文档 索引结构文档
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
Range和Index也适用于切片数组之外,例如循环
Range range = 1..4;
foreach (var name in names[range])
将循环1到4项
请注意,在撰写本文时,c# 8.0还没有正式发布 c# 8。. x和。net Core 3。x现已在Visual Studio 2019及以后版本中可用