Harry Stuart
Harry Stuart

Reputation: 1939

Convert byte array to array segments of a certain length

I have a byte array and I would like to return sequential chuncks (in the form of new byte arrays) of a certain size.

I tried:

originalArray = BYTE_ARRAY
var segment = new ArraySegment<byte>(originalArray,0,640); 
byte[] newArray = new byte[640];
for (int i = segment.Offset; i <= segment.Count; i++)
{
newArray[i] = segment.Array[i];
}

Obviously this only creates an array of the first 640 bytes from the original array. Ultimately, I want a loop that goes through the first 640 bytes and returns an array of those bytes, then it goes through the NEXT 640 bytes and returns an array of THOSE bytes. The purpose of this is to send messages to a server and each message must contain 640 bytes. I cannot garauntee that the original array length is divisible by 640.

Thanks

Upvotes: 1

Views: 3599

Answers (3)

Matthew Watson
Matthew Watson

Reputation: 109577

You can write a generic helper method like this:

public static IEnumerable<T[]> AsBatches<T>(T[] input, int n)
{
    for (int i = 0, r = input.Length; r >= n; r -= n, i += n)
    {
        var result = new T[n];
        Array.Copy(input, i, result, 0, n);
        yield return result;
    }
}

Then you can use it in a foreach loop:

byte[] byteArray = new byte[123456];

foreach (var batch in AsBatches(byteArray, 640))
{
    Console.WriteLine(batch.Length); // Do something with the batch.
}

Or if you want a list of batches just do this:

List<byte[]> listOfBatches = AsBatches(byteArray, 640).ToList();

If you want to get fancy you could make it an extension method, but this is only recommended if you will be using it a lot (don't make an extension method for something you'll only be calling in one place!).

Here I've changed the name to InChunksOf() to make it more readable:

public static class ArrayExt
{
    public static IEnumerable<T[]> InChunksOf<T>(this T[] input, int n)
    {
        for (int i = 0, r = input.Length; r >= n; r -= n, i += n)
        {
            var result = new T[n];
            Array.Copy(input, i, result, 0, n);
            yield return result;
        }
    }
}

Which you could use like this:

byte[] byteArray = new byte[123456];

// ... initialise byteArray[], then:

var listOfChunks = byteArray.InChunksOf(640).ToList();

[EDIT] Corrected loop terminator from r > n to r >= n.

Upvotes: 0

TheGeneral
TheGeneral

Reputation: 81503

if speed isn't a concern

var bytes = new byte[640 * 6];

for (var i = 0; i <= bytes.Length; i+=640)
{
   var chunk = bytes.Skip(i).Take(640).ToArray();
   ...
}

Alternatively you could use

Span

Span<byte> bytes = arr; // Implicit cast from T[] to Span<T>

...

slicedBytes = bytes.Slice(i, 640);

BlockCopy

Note this will probably be the fastest of the 3

var chunk = new byte[640]
Buffer.BlockCopy(bytes, i, chunk, 0, 640);

Upvotes: 2

clarkitect
clarkitect

Reputation: 1730

If you truly want to make new arrays from each 640 byte chunk, then you're looking for .Skip and .Take

Here's a working example (and a repl of the example) that I hacked together.

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

class MainClass {
public static void Main (string[] args) {
        // mock up a byte array from something
        var seedString = String.Join("", Enumerable.Range(0, 1024).Select(x => x.ToString()));
        var byteArrayInput = Encoding.ASCII.GetBytes(seedString);

        var skip = 0;
        var take = 640;
        var total = byteArrayInput.Length;

        var output = new List<byte[]>();

        while (skip + take < total) {
            output.Add(byteArrayInput.Skip(skip).Take(take).ToArray());
            skip += take;
        }

        output.ForEach(c => Console.WriteLine($"chunk: {BitConverter.ToString(c)}"));
    }
}

It's really probably better to actually use the ArraySegment properly --unless this is an assignment to learn LINQ extensions.

Upvotes: 0

Related Questions