Reputation: 132254
How do you do it? Given a byte array:
byte[] foo = new byte[4096];
How would I get the first x bytes of the array as a separate array? (Specifically, I need it as an IEnumerable<byte>
)
This is for working with Socket
s. I figure the easiest way would be array slicing, similar to Perls syntax:
@bar = @foo[0..40];
Which would return the first 41 elements into the @bar
array. Is there something in C# that I'm just missing, or is there some other thing I should be doing?
LINQ is an option for me (.NET 3.5), if that helps any.
Upvotes: 313
Views: 440966
Reputation: 3553
Often when reading from streams you want to handle reading less that requested.
Array.Resize is your friend in this situation.
Upvotes: 0
Reputation: 6658
C# 8 now (since 2019) supports Ranges which allows you to achieve Slice much easier (similar to JS syntax):
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]
You can use ranges instead of the well known LINQ functions: Skip(), Take(), Count().
Upvotes: 30
Reputation: 8140
You could use a wrapper around the original array (which is IList), like in this (untested) piece of code.
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
}
Upvotes: 7
Reputation: 5163
Starting from C# 8.0/.Net Core 3.0
Array slicing will be supported, along with the new types Index
and Range
being added.
Range Struct docs
Index Struct docs
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 }
Above code sample taken from the C# 8.0 blog.
note that the ^
prefix indicates counting from the end of the array. As shown in the docs example
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
and Index
also work outside of slicing arrays, for example with loops
Range range = 1..4;
foreach (var name in names[range])
Will loop through the entries 1 through 4
note that at the time of writing this answer, C# 8.0 is not yet officially released
C# 8.x and .Net Core 3.x are now available in Visual Studio 2019 and onwards
Upvotes: 91
Reputation: 11
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();
}
}
}
Upvotes: 0
Reputation: 156968
In C# 7.2, you can use Span<T>
. The benefit of the new System.Memory
system is that it doesn't need copying around data.
The method you need is Slice
:
Span<byte> slice = foo.Slice(0, 40);
A lot of methods now support Span
and IReadOnlySpan
, so it will be very straightforward to use this new type.
Note that at the time of writing the Span<T>
type is not defined in the the most recent version of .NET yet (4.7.1) so to use it you need to install the System.Memory package from NuGet.
Upvotes: 29
Reputation: 6360
Arrays are enumerable, so your foo
already is an IEnumerable<byte>
itself.
Simply use LINQ sequence methods like Take()
to get what you want out of it (don't forget to include the Linq
namespace with using System.Linq;
):
byte[] foo = new byte[4096];
var bar = foo.Take(41);
If you really need an array from any IEnumerable<byte>
value, you could use the ToArray()
method for that. That does not seem to be the case here.
Upvotes: 245
Reputation: 13670
If you don't want to add LINQ or other extensions just do:
float[] subArray = new List<float>(myArray).GetRange(0, 8).ToArray();
Upvotes: 10
Reputation: 13672
You could use the arrays CopyTo()
method.
Or with LINQ you can use Skip()
and Take()
...
byte[] arr = {1, 2, 3, 4, 5, 6, 7, 8};
var subset = arr.Skip(2).Take(2);
Upvotes: 173
Reputation: 1667
I do not think C# supports the Range semantics. You could write an extension method though, like:
public static IEnumerator<Byte> Range(this byte[] array, int start, int end);
But like others have said if you do not need to set a start index then Take
is all you need.
Upvotes: 2
Reputation: 796
For byte arrays System.Buffer.BlockCopy will give you the very best performance.
Upvotes: 7
Reputation: 11
Here is an extension function that uses a generic and behaves like the PHP function array_slice. Negative offset and length are allowed.
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;
}
}
Upvotes: 1
Reputation: 95
This may be a solution that:
var result = foo.Slice(40, int.MaxValue);
Then the result is an IEnumerable< IEnumerable< byte>> with a first IEnumerable< byte> contains the first 40 bytes of foo, and a second IEnumerable< byte> holds the rest.
I wrote a wrapper class, the whole iteration is lazy, hope it could help:
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;
}
Upvotes: 3
Reputation: 1810
Here's a simple extension method that returns a slice as a new array:
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;
}
Then you can use it as:
byte[] slice = foo.Slice(0, 40);
Upvotes: 14
Reputation: 5393
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);
Upvotes: 60
Reputation: 20445
Another possibility I haven't seen mentioned here: Buffer.BlockCopy() is slightly faster than Array.Copy(), and it has the added benefit of being able to convert on-the-fly from an array of primitives (say, short[]) to an array of bytes, which can be handy when you've got numeric arrays that you need to transmit over Sockets.
Upvotes: 17
Reputation: 12463
You could use ArraySegment<T>
. It's very light-weight as it doesn't copy the array:
string[] a = { "one", "two", "three", "four", "five" };
var segment = new ArraySegment<string>( a, 1, 2 );
Upvotes: 254
Reputation: 123966
You can use Take extension method
var array = new byte[] {1, 2, 3, 4};
var firstTwoItems = array.Take(2);
Upvotes: 5
Reputation: 1062745
If you want IEnumerable<byte>
, then just
IEnumerable<byte> data = foo.Take(x);
Upvotes: 15