Массивные срезы в С#
Как вы это делаете? Учитывая массив байтов:
byte[] foo = new byte[4096];
Как получить первые x байтов массива в виде отдельного массива? (В частности, мне это нужно как IEnumerable<byte>
)
Это для работы с Socket
s. Я считаю, что самый простой способ - массивы, похожие на синтаксис Perls:
@bar = @foo[0..40];
Что вернет первые 41 элемент в массив @bar
. Есть ли что-то в С#, которое я просто отсутствую, или есть еще кое-что, что я должен делать?
LINQ - это вариант для меня (.NET 3.5), если это помогает любому.
Ответы
Ответ 1
Массивы перечислимы, поэтому ваш foo
уже является IEnumerable<byte>
.
Просто используйте методы последовательности LINQ, такие как Take()
, чтобы получить то, что вы хотите от него (не забудьте включить пространство имен Linq
с using System.Linq;
):
byte[] foo = new byte[4096];
var bar = foo.Take(41);
Если вам действительно нужен массив из любого значения IEnumerable<byte>
, для этого вы можете использовать метод ToArray()
. Это, похоже, не так.
Ответ 2
Вы можете использовать ArraySegment<T>
. Он очень легкий, так как он не копирует массив:
string[] a = { "one", "two", "three", "four", "five" };
var segment = new ArraySegment<string>( a, 1, 2 );
Ответ 3
Вы можете использовать метод массивов CopyTo()
.
Или с помощью LINQ вы можете использовать Skip()
и Take()
...
byte[] arr = {1, 2, 3, 4, 5, 6, 7, 8};
var subset = arr.Skip(2).Take(2);
Ответ 4
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);
Ответ 5
Еще одна возможность, о которой я не упоминал здесь: Buffer.BlockCopy() немного быстрее, чем Array.Copy(), и у нее есть дополнительное преимущество, заключающееся в возможности "на лету" конвертировать из массива примитивов ( скажем, short []) в массив байтов, что может быть удобно, когда у вас есть числовые массивы, которые вам нужно передать через сокеты.
Ответ 6
Вот простой метод расширения, который возвращает срез как новый массив:
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);
Ответ 7
Если вы хотите IEnumerable<byte>
, то просто
IEnumerable<byte> data = foo.Take(x);
Ответ 8
В С# 7.2 вы можете использовать Span<T>
. Преимущество новой системы System.Memory
заключается в том, что ей не нужно копировать данные.
Вам нужен метод Slice
:
Span<byte> slice = foo.Slice(0, 40);
Многие методы теперь поддерживают Span
и IReadOnlySpan
, поэтому использовать этот новый тип будет очень просто.
Обратите внимание, что на момент написания статьи тип Span<T>
еще не определен в самой последней версии .NET(4.7.1), поэтому для его использования необходимо установить пакет System.Memory из NuGet.
Ответ 9
Начиная с С# 8.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 }
префикс ^
указывает на отсчет с конца массива.
Выше пример кода взят из блога С# 8.0.
Range
и Index
также работают с циклами foreach
Range range = 1..4;
foreach (var name in names[range])
Перебирает записи с 1 по 4
обратите внимание, что на момент написания этого ответа С# 8.0 еще не был официально выпущен
Ответ 10
Если вы не хотите добавлять LINQ или другие расширения, просто выполните:
float[] subArray = new List<float>(myArray).GetRange(0, 8).ToArray();
Ответ 11
Вы можете использовать обертку вокруг исходного массива (это 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
}
Ответ 12
byte[] foo = new byte[4096];
byte[] bar = foo.Take(40).ToArray();
Ответ 13
Вы можете использовать метод расширенного расширения
var array = new byte[] {1, 2, 3, 4};
var firstTwoItems = array.Take(2);
Ответ 14
Для байтовых массивов System.Buffer.BlockCopy обеспечит вам наилучшую производительность.
Ответ 15
Это может быть решение, которое:
var result = foo.Slice(40, int.MaxValue);
Тогда результат является IEnumerable < IEnumerable < байт → с первым IEnumerable <byte> содержит первые 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;
}
Ответ 16
Я не думаю, что С# поддерживает семантику Range. Вы могли бы написать метод расширения, хотя:
public static IEnumerator<Byte> Range(this byte[] array, int start, int end);
Но, как и другие, сказали, что вам не нужно устанавливать начальный индекс, тогда Take
- это все, что вам нужно.
Ответ 17
Вот функция расширения, которая использует общий тип и ведет себя как функция 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;
}
}
Ответ 18
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();
}
}
}
Ответ 19
Начиная с С# 8.0, срез массива доступен. https://blogs.msdn.microsoft.com/dotnet/2018/12/05/take-c-8-0-for-a-spin/