37

This is a simple one, and one that I thought would have been answered. I did try to find an answer on here, but didn't come up with anything - so apologies if there is something I have missed.

Anyway, is there an equivalent of StringBuilder but for byte arrays?

I'm not bothered about all the different overloads of Append() - but I'd like to see Append(byte) and Append(byte[]).

Is there anything around or is it roll-your-own time?

Matt Whitfield
  • 6,158
  • 3
  • 26
  • 42

5 Answers5

38

Would MemoryStream work for you? The interface might not be quite as convenient, but it offers a simple way to append bytes, and when you are done you can get the contents as a byte[] by calling ToArray().

A more StringBuilder-like interface could probably be achieved by making an extension method.

Update
Extension methods could look like this:

public static class MemoryStreamExtensions
{
    public static void Append(this MemoryStream stream, byte value)
    {
        stream.Append(new[] { value });
    }

    public static void Append(this MemoryStream stream, byte[] values)
    {
        stream.Write(values, 0, values.Length);
    }
}

Usage:

MemoryStream stream = new MemoryStream();
stream.Append(67);
stream.Append(new byte[] { 68, 69 });
byte[] data = stream.ToArray();  // gets an array with bytes 67, 68 and 69
Fredrik Mörk
  • 147,210
  • 26
  • 277
  • 333
  • Yes it would - I like that approach... Thanks – Matt Whitfield Oct 25 '10 at 14:43
  • Can I just ask - I haven't actually seen the syntax `new[] { value }` before - is that inferring the type of the array? – Matt Whitfield Oct 25 '10 at 14:51
  • 2
    @Matt: yes, that is correct. In the extension method, `value` is of type `byte`, so `byte[]` is inferred. In the last sample I need to specify `new byte[]` since the compiler would infer the type `int` otherwise. – Fredrik Mörk Oct 25 '10 at 14:53
  • @Frederik Mörk - Awesome - thanks. Accepting for the extra 'I've learned something new today' goodness. – Matt Whitfield Oct 25 '10 at 14:55
  • 1
    @Fredrik Mörk: Is there any way to munch data from the start of a MemoryStream, so it works like a queue, or is there any other CLR class which can be used as an intra-process pipe? – supercat Oct 25 '10 at 15:00
  • 1
    @supercat: as far as I know, there is no functionality in `MemoryStream` for that. I don't know any other class than [`Queue`](http://msdn.microsoft.com/en-us/library/7977ey2c.aspx) from the top of my head. – Fredrik Mörk Oct 25 '10 at 15:11
  • 1
    To make this even more like a StringBuilder, you could have the extension methods return the stream, itself. While this may sacrifice a small amount of efficiency, it's not terribly uncommon in builder patterns to return the builder with each method, so that they can be called in one, long statement. – Gurgadurgen Dec 16 '14 at 17:26
  • It'd be more efficient to use `stream.WriteByte(value)` for the `Append` overload that takes a single byte as it would avoid the intermediate `byte[]` array allocation. – Justin Van Patten Jul 11 '16 at 22:38
  • Don't you need to position MemoryStream to the end before appending? – AaA May 02 '17 at 07:30
21

The MemoryStream approach is good, but if you want to have StringBuilder-like behavior add a BinaryWriter. BinaryWriter provides all the Write overrides you could want.

MemoryStream stream = new MemoryStream();
BinaryWriter writer = new BinaryWriter(stream);
writer.Write((byte)67);
writer.Write(new byte[] { 68, 69 });
Jon
  • 7,185
  • 6
  • 45
  • 66
Tergiver
  • 13,473
  • 2
  • 34
  • 64
12

Probably List<byte>:

var byteList = new List<byte>();
byteList.Add(42);
byteList.AddRange(new byte[] { 1, 2, 3 });
Greg
  • 21,917
  • 11
  • 55
  • 77
LukeH
  • 242,140
  • 52
  • 350
  • 400
  • ...and then Append(byte) becomes Add(byte) and Append(byte[]) becomes AddRange(byte[]). – Justin Niessner Oct 25 '10 at 14:39
  • Note that as with `StringBuilder`, adding elements to a `List` is amortized O(1). – Brian Oct 25 '10 at 14:43
  • Ok, cool - just one question... Would that be as efficient? I know List<> does some efficient copies for AddRange under the hood, but I didn't know if that works out as the most efficient way to do it? – Matt Whitfield Oct 25 '10 at 14:43
  • @Matt : As with `StringBuilder`, C# `List` uses a backing capacity greater than the size of the data structure in order to enable constant time insertions. This capacity is doubled as needed. This strategy is the same strategy `StringBuilder` uses to avoid excessive memory allocations. – Brian Oct 25 '10 at 14:50
  • @Brian - Actually, what I was referring to was the way that List<> checks to see if the IEnumerable it is adding in AddRange is an ICollection too, and if it is it adds the elements in a different way... I can't remember exactly how, because I'm not at a machine where I have the reference code downloaded... – Matt Whitfield Oct 25 '10 at 14:54
  • 1
    @Matt: Yes, if you pass an `ICollection` then the `AddRange` method uses the collection's `Count` property in order pre-allocate the list to the right size (if necessary). If it's not an `ICollection` then the list will need to be resized on-the-fly as the elements are added. – LukeH Oct 25 '10 at 15:46
  • How does your answer compare to Fredrik Mörk's 'MemoryStream' answer in terms of versatility, efficiency and closeness to StringBuilder? – Dan W Jun 19 '13 at 21:54
  • One notable feature of `StringBuilder` that `MemoryStream` is missing is `StringBuilder.Remove()`. `List` appears to provide an exact equivalent with `List.RemoveRange()`. The inflexibility of `MemoryStream`’s API makes me suspect that it can be more efficient on operations both it and `List` support, but I haven’t tested this. – binki Mar 12 '14 at 23:59
2

List<byte> Then when you want it as an array you can call ToArray()

Aaron McIver
  • 23,797
  • 5
  • 53
  • 83
0
using System;
using System.IO;

public static class MemoryStreams
{
    public static MemoryStream Append(
        this MemoryStream stream
        , byte value
        , out bool done)
    {
        try
        {
            stream.WriteByte(value);
            done = true;
        }
        catch { done = false; }
        return stream;
    }

    public static MemoryStream Append(
        this MemoryStream stream
        , byte[] value
        , out bool done
        , uint offset = 0
        , Nullable<uint> count = null)
    {
        try
        {
            var rLenth = (uint)((value == null) ? 0 : value.Length);

            var rOffset = unchecked((int)(offset & 0x7FFFFFFF));

            var rCount = unchecked((int)((count ?? rLenth) & 0x7FFFFFFF));

            stream.Write(value, rOffset, rCount);
            done = true;
        }
        catch { done = false; }
        return stream;
    }
}
MStack
  • 278
  • 3
  • 5