40

I have a byte array in memory, read from a file. I would like to split the byte array at a certain point (index) without having to just create a new byte array and copy each byte at a time, increasing the in memory foot print of the operation. What I would like is something like this:

byte[] largeBytes = [1,2,3,4,5,6,7,8,9];  
byte[] smallPortion;  
smallPortion = split(largeBytes, 3);  

smallPortion would equal 1,2,3,4
largeBytes would equal 5,6,7,8,9

8 Answers 8

30

In C# with Linq you can do this:

smallPortion = largeBytes.Take(4).ToArray();
largeBytes = largeBytes.Skip(4).Take(5).ToArray();

;)

Sign up to request clarification or add additional context in comments.

4 Comments

The OP is wondering how to do this without having to [...] create a new byte array and copy each byte at a time But that is exactly what your LINQ code does. Twice.
..this helped me solve a problem I had that didn't get solved by ArraySegment<T>..
@Christian, to avoid using adittional memory just remove both ".ToArray()" calls. That will return two IEnumerables with a lot less lines of code.
@GerardoGrignoli true, but that gives you an IEnumerable<byte>. While it is perfectly safe (and fast) to iterate over that enumerable multiple times there are two downsides: a) you still can't index into that section of the array directly. Sure, the LINQ implementations of Skip/Take take advantage of the array structure, but they do so by trying to cast the IEnumerable to Collection or Array. b) Once you return an IEnumerable from your API, clients are no longer guaranteed (by the type system) that the enumerable is safe and efficient to iterate over multiple times.
22

FYI. System.ArraySegment<T> structure basically is the same thing as ArrayView<T> in the code above. You can use this out-of-the-box structure in the same way, if you'd like.

1 Comment

Interesting. Too bad I didn't see this one when I was working on that project. Thanks for the info anyway.
16

This is how I would do that:

using System;
using System.Collections;
using System.Collections.Generic;

class ArrayView<T> : IEnumerable<T>
{
    private readonly T[] array;
    private readonly int offset, count;

    public ArrayView(T[] array, int offset, int count)
    {
        this.array = array;
        this.offset = offset;
        this.count = count;
    }

    public int Length
    {
        get { return count; }
    }

    public T this[int index]
    {
        get
        {
            if (index < 0 || index >= this.count)
                throw new IndexOutOfRangeException();
            else
                return this.array[offset + index];
        }
        set
        {
            if (index < 0 || index >= this.count)
                throw new IndexOutOfRangeException();
            else
                this.array[offset + index] = value;
        }
    }

    public IEnumerator<T> GetEnumerator()
    {
        for (int i = offset; i < offset + count; i++)
            yield return array[i];
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        IEnumerator<T> enumerator = this.GetEnumerator();
        while (enumerator.MoveNext())
        {
            yield return enumerator.Current;
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        byte[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
        ArrayView<byte> p1 = new ArrayView<byte>(arr, 0, 5);
        ArrayView<byte> p2 = new ArrayView<byte>(arr, 5, 5);
        Console.WriteLine("First array:");
        foreach (byte b in p1)
        {
            Console.Write(b);
        }
        Console.Write("\n");
        Console.WriteLine("Second array:");
        foreach (byte b in p2)
        {
            Console.Write(b);
        }
        Console.ReadKey();
    }
}

Comments

3

Try this one:

private IEnumerable<byte[]> ArraySplit(byte[] bArray, int intBufforLengt)
    {
        int bArrayLenght = bArray.Length;
        byte[] bReturn = null;

        int i = 0;
        for (; bArrayLenght > (i + 1) * intBufforLengt; i++)
        {
            bReturn = new byte[intBufforLengt];
            Array.Copy(bArray, i * intBufforLengt, bReturn, 0, intBufforLengt);
            yield return bReturn;
        }

        int intBufforLeft = bArrayLenght - i * intBufforLengt;
        if (intBufforLeft > 0)
        {
            bReturn = new byte[intBufforLeft];
            Array.Copy(bArray, i * intBufforLengt, bReturn, 0, intBufforLeft);
            yield return bReturn;
        }
    }

1 Comment

I think it should be static
2

As Eren said, you can use ArraySegment<T>. Here's an extension method and usage example:

public static class ArrayExtensionMethods
{
    public static ArraySegment<T> GetSegment<T>(this T[] arr, int offset, int? count = null)
    {
        if (count == null) { count = arr.Length - offset; }
        return new ArraySegment<T>(arr, offset, count.Value);
    }
}

void Main()
{
    byte[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
    var p1 = arr.GetSegment(0, 5);
    var p2 = arr.GetSegment(5);
    Console.WriteLine("First array:");
    foreach (byte b in p1)
    {
        Console.Write(b);
    }
    Console.Write("\n");
    Console.WriteLine("Second array:");
    foreach (byte b in p2)
    {
        Console.Write(b);
    }
}

Comments

1

I'm not sure what you mean by:

I would like to split the byte array at a certain point(index) without having to just create a new byte array and copy each byte at a time, increasing the in memory foot print of the operation.

In most languages, certainly C#, once an array has been allocated, there is no way to change the size of it. It sounds like you're looking for a way to change the length of an array, which you can't. You also want to somehow recycle the memory for the second part of the array, to create a second array, which you also can't do.

In summary: just create a new array.

Comments

0

You can't. What you might want is keep a starting point and number of items; in essence, build iterators. If this is C++, you can just use std::vector<int> and use the built-in ones.

In C#, I'd build a small iterator class that holds start index, count and implements IEnumerable<>.

Comments

0

I tried different algorithms :

  • Skip().Take() => the worst, by far
  • Array.Copy
  • ArraySegment
  • new Guid(int, int16, int16 ...)

The latest being the fastest I'm now using this extension method:

        public static Guid ToGuid(this byte[] byteArray, int offset)
        {
            return new Guid(BitConverter.ToInt32(byteArray, offset), BitConverter.ToInt16(byteArray, offset + 4), BitConverter.ToInt16(byteArray, offset + 6), byteArray[offset + 8], byteArray[offset + 9], byteArray[offset + 10], byteArray[offset + 11], byteArray[offset + 12], byteArray[offset + 13], byteArray[offset + 14], byteArray[offset + 15]);
        }

With a byte array with 10000000 guids:

Done (Skip().Take()) in 1,156ms (for only 100000 guids :))
Done (Array.Copy) in 1,219ms
Done (ToGuid extension) in 994ms
Done (ArraySegment) in 2,411ms

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.