Can a Byte[] Array Be Written to a File in C#

Can a Byte[] Array be written to a file in C#?

Based on the first sentence of the question: "I'm trying to write out a Byte[] array representing a complete file to a file."

The path of least resistance would be:

File.WriteAllBytes(string path, byte[] bytes)

Documented here:

System.IO.File.WriteAllBytes - MSDN

Save byte array to file

You can use:

File.WriteAllBytes("Foo.txt", arrBytes); // Requires System.IO

If you have an enumerable and not an array, you can use:

File.WriteAllBytes("Foo.txt", arrBytes.ToArray()); // Requires System.Linq

Write and read the multiple byte[] into the file C#

You could use a BinaryWriter and a BinaryReader as shown below. First write the length of the array as an int32, then write the array bytes. Repeat for the second array. In reverse, read the length of the array as an int32, then read that many bytes. Repeat for the second array:

byte[] source = new byte[2] { 1, 2 };
byte[] dest = new byte[6] { 2, 4, 8, 16, 32, 64 };

using (FileStream fs = new FileStream("myFile.txt", FileMode.OpenOrCreate))
{
using (BinaryWriter bw = new BinaryWriter(fs))
{
bw.Write(source.Length);
bw.Write(source, 0, source.Length);
bw.Write(dest.Length);
bw.Write(dest, 0, dest.Length);
}
}

byte[] source2;
byte[] dest2;
using (FileStream fs = new FileStream("myFile.txt", FileMode.Open))
{
using (BinaryReader br = new BinaryReader(fs))
{
source2 = br.ReadBytes(br.ReadInt32());
dest2 = br.ReadBytes(br.ReadInt32());
}
}

Console.WriteLine("source = " + String.Join(" ", source));
Console.WriteLine("dest = " + String.Join(" ", dest));
Console.WriteLine("source2 = " + String.Join(" ", source2));
Console.WriteLine("dest2 = " + String.Join(" ", dest2));

Output:

source = 1 2
dest = 2 4 8 16 32 64
source2 = 1 2
dest2 = 2 4 8 16 32 64

How to write to file with a pointer (byte*) to an array of byte

Disregarding any other problems (conceptual or otherwise). There are a few ways to do this.

Convoluted examples ensue

If you could use Span<T> which can take a pointer and length and then use FileStream.Write(ReadOnlySpan<Byte>) overload

Writes a sequence of bytes from a read-only span to the current file
stream and advances the current position within this file stream by
the number of bytes written.

var bytes = new byte[] {1,2,3};
var size = bytes.Length;

using var fs = new FileStream(@"SomeAwesomeFileNamedBob.dat", FileMode.Create);

fixed (byte* p = bytes)
{
var span = new Span<byte>(p, size);
fs.Write(span);
}

Or, just use BinaryWriter.Write and write each byte, this is a̶ ̶l̶i̶t̶t̶l̶e̶ ... extremely inefficient

Writes a signed byte to the current stream and advances the stream
position by one byte.

var bytes = new byte[] {1, 2, 3};
var size = bytes.Length;

using var fs = new FileStream(@"SomeAwesomeFileNamedBob.dat", FileMode.Create);
using var bw = new BinaryWriter(fs);

fixed (byte* p = bytes)
for (int i = 0; i < size; i++)
bw.Write(*p);

Or, at the cost of an allocation, just Buffer.MemoryCopy to a new array and Write

Copies a block of memory.

var bytes = new byte[] {1,2,3};
var size = bytes.Length;

using var fs = new FileStream(@"SomeAwesomeFileNamedBob.dat", FileMode.Create);

var temp = new byte[size];
fixed (byte* pOld = bytes,pNew = temp)
{
Buffer.MemoryCopy(pOld,pNew,size,size);
fs.Write(temp,0,size);
}

Or, expanding on the array copy method, you could use an ArrayPool<Byte> for fewer allocations and in-turn will be better for your LOH (if applicable)

Provides a resource pool that enables reusing instances of type T[].

private static readonly ArrayPool<byte> _pool = ArrayPool<byte>.Shared;

...

var size = bytes.Length;

using var fs = new FileStream(@"SomeAwesomeFileNamedBob.dat", FileMode.Create);

var temp = _pool.Rent(size);
try
{
fixed (byte* pOld = bytes, pNew = temp)
{
Buffer.MemoryCopy(pOld, pNew, size, size);
fs.Write(temp, 0, size);
}
}
finally
{
_pool.Return(temp);
}

Or you could use an UnmanagedMemoryStream

Provides access to unmanaged blocks of memory from managed code.

Important

This API is not CLS-compliant.

var bytes = new byte[] {1,2,3};
var size = bytes.Length;

using var fs = new FileStream(@"SomeAwesomeFileNamedBob.dat", FileMode.Create);

fixed (byte* p = bytes)
{
using var us = new UnmanagedMemoryStream(p,size);
us.CopyTo(fs);
}


Benchmarks

BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.630 (2004/?/20H1)
Intel Core i7-7700 CPU 3.60GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=5.0.100
[Host] : .NET Core 5.0.0 (CoreCLR 5.0.20.51904, CoreFX 5.0.20.51904), X64 RyuJIT [AttachedDebugger]
.NET Core 5.0 : .NET Core 5.0.0 (CoreCLR 5.0.20.51904, CoreFX 5.0.20.51904), X64 RyuJIT

Job=.NET Core 5.0 Runtime=.NET Core 5.0

| Method | _size | Mean | Error | StdDev | Median |
|---------------- |------- |-------------:|-------------:|-------------:|-------------:|
| Span | 1000 | 122.4 ns | 2.46 ns | 2.42 ns | 122.7 ns |
| Single | 1000 | 5,548.3 ns | 82.61 ns | 73.23 ns | 5,561.8 ns |
| NewArray | 1000 | 230.4 ns | 4.64 ns | 10.56 ns | 227.4 ns |
| ArrayPool | 1000 | 185.6 ns | 3.74 ns | 4.60 ns | 186.1 ns |
| UnmanagedStream | 1000 | 249.8 ns | 4.89 ns | 8.69 ns | 247.5 ns |
|---------------- |------- |-------------:|-------------:|-------------:|-------------:|
| Span | 10000 | 1,012.9 ns | 20.06 ns | 44.87 ns | 1,007.0 ns |
| Single | 10000 | 56,143.2 ns | 980.01 ns | 1,436.48 ns | 56,087.6 ns |
| NewArray | 10000 | 2,086.1 ns | 43.89 ns | 127.34 ns | 2,048.9 ns |
| ArrayPool | 10000 | 1,277.2 ns | 24.38 ns | 50.88 ns | 1,272.3 ns |
| UnmanagedStream | 10000 | 1,267.8 ns | 24.52 ns | 28.24 ns | 1,260.9 ns |
|---------------- |------- |-------------:|-------------:|-------------:|-------------:|
| Span | 100000 | 56,843.0 ns | 1,107.92 ns | 1,137.75 ns | 56,587.5 ns |
| Single | 100000 | 601,186.9 ns | 11,991.48 ns | 17,576.95 ns | 598,002.9 ns |
| NewArray | 100000 | 111,234.1 ns | 1,296.51 ns | 1,012.23 ns | 111,268.3 ns |
| ArrayPool | 100000 | 59,183.1 ns | 278.01 ns | 232.15 ns | 59,141.8 ns |
| UnmanagedStream | 100000 | 58,539.6 ns | 941.79 ns | 834.87 ns | 58,176.1 ns |

Setup

[SimpleJob(RuntimeMoniker.NetCoreApp50)]
public unsafe class DumbTest
{
[Params(1000, 10000, 100000)] public int _size;

private byte* _p;
private GCHandle _handle;
private readonly ArrayPool<byte> _pool = ArrayPool<byte>.Shared;

[GlobalSetup]
public void Setup()
{
var bytes = new byte[_size];
new Random(42).NextBytes(bytes);
_handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
_p = (byte*) _handle.AddrOfPinnedObject();
}

[GlobalCleanup]
public void Cleanup() => _handle.Free();

[Benchmark]
public void Span()
{
using var ms = new MemoryStream();
var span = new Span<byte>(_p, _size);
ms.Write(span);
}

[Benchmark]
public void Single()
{
using var ms = new MemoryStream();
using var bw = new BinaryWriter(ms);
for (var i = 0; i < _size; i++)
bw.Write(*_p);
}

[Benchmark]
public void NewArray()
{
using var ms = new MemoryStream();

var temp = new byte[_size];
fixed (byte* pNew = temp)
{
Buffer.MemoryCopy(_p, pNew, _size, _size);
ms.Write(temp, 0, _size);
}
}

[Benchmark]
public void ArrayPool()
{
using var ms = new MemoryStream();

var temp = _pool.Rent(_size);
try
{
fixed (byte* pNew = temp)
{
Buffer.MemoryCopy(_p,pNew,_size,_size);
ms.Write(temp,0,_size);
}
}
finally
{
_pool.Return(temp);
}
}
[Benchmark]
public void UnmanagedStream()
{
using var ms = new MemoryStream();
using var us = new UnmanagedMemoryStream(_p, _size);
us.CopyTo(ms);

}
}

Write to file from byte array in c#

Try closing or disposing the BinaryWriter, e.g.

using (var bw = new BinaryWriter(File.Open("c:\\Tmp\\" + a.Name, FileMode.OpenOrCreate)))
{
bw.Write(((FileAttachment)a).Content);
}

Also, you might want to use Path.Combine() from now on (it will take care about the backslashes) and use @ to make directory names readable:

Path.Combine(@"c:\Tmp\", a.Name);


Related Topics



Leave a reply



Submit