vtortola.Net

Mayo 9, 2007

Leyendo datos binarios en C#

Archivado en: .NET, C#, Input Output — vtortola @ 8:44 am
Este artículo es una traducción de Reading binary data in C# de Jon Skeet, el cual me ha dado su consentimiento expreso para traducirlo y ponerlo aquí.
Thanks Jon ;)

En los newsgroup de C#, he visto bastantes veces código para leer datos de un archivo como este:

// Mal método! No lo uses!
FileStream fs = File.OpenRead(filename);
byte[] data = new byte[fs.Length];
fs.Read(data, 0, data.Length);

Este código esta lejos de garantizar su funcionamiento. En concreto, FileStream podría estar leyendo solo los primeros 10bytes del archivo en el buffer. El método Read solo está garantizado para bloquear hasta que alguna porción de datos esta disponible (o el final del stream, si se alcanzó), no hasta que la totalidad de los datos están disponibles. Aquí es donde el valor de retorno (el cual es ignorado en el código de arriba) es vital. Necesitas arreglártelas para el caso en que no puedas leer todos los datos en la misma pasada, y hacer un bucle hasta que hayas leído todo lo quieres. Aquí un método que puedes usar si quieres leer desde un stream a un array sin parar hasta que este haya finalizado:

/// <summary>
/// Lee datos a un array completo, lanzando una EndOfStreamException
/// si el stream se sale de los datos o si una IOException natural ocurre.
/// </summary>
/// <param name=”stream”>Stream del que leer.</param>
/// <param name=”data”>El array donde introducir los datos.
/// El array quedará completamente lleno del stream, por eso
/// se le debe dar el tamaño apropiado.</param>
public static void ReadWholeArray(Stream stream, byte[] data)
{

int offset = 0;
int remaining = data.Length;
while (remaining > 0)
{

int read = stream.Read(data, offset, remaining);
if (read <= 0)
throw new EndOfStreamException (String.Format(“End of stream reached with {0} bytes left to read”, remaining));
remaining -= read;
offset += read;

}

}

Hay veces que no sabes la cantidad de datos del stream de antemano (por ejemplo un NetworkStream) y solo quieres leer lo que haya en el buffer. Aquí un método que hace justo eso:

/// <summary>
/// Lee datos de un stream hasta el final.
/// Los datos son devueltos como un byte[].
/// Una IOException sucederá si alguna llamada
/// de IO subyacente falla.
/// </summary>
/// <param name=”stream”>El stream del que leer.</param>
public static byte[] ReadFully(Stream stream)
{

byte[] buffer = new byte[32768];
using (MemoryStream ms = new MemoryStream())
{

while (true)
{

int read = stream.Read(buffer, 0, buffer.Length);
if (read <= 0) return ms.ToArray();
ms.Write(buffer, 0, read);

}

}

}

Mientras el de arriba es simple, no es excesivamente eficiente, ya que termina copiando los datos al final, y probablemente algunas veces entre medias. Aquí un método el cual funciona bien si conoces la cantidad esperada de datos con la que empezar (Mientras prodrías usar Stream.Length, no esta soportado por todos los streams)

/// <summary>
/// Lee datos del stream hasta el final.
/// Los datos son devueltos como un byte[].
/// Una IOException sucederá si alguna llamada
/// de IO subyacente falla.
/// </summary>
/// <param name=”stream”>El stream del que leer.</param>
/// <param name=”initialLength”>Longitud inicial del buffer.</param>
public static byte[] ReadFully(Stream stream, int initialLength)
{

// Si vemos que se pasa una cantidad burda de buffer
// usamos 32K.
if (initialLength < 1)
{

initialLength = 32768;

}

byte[] buffer = new byte[initialLength];
int read = 0;

int chunk;
while ((chunk = stream.Read(buffer, read, buffer.Length – read)) > 0)
{

read += chunk;

// Si hemos alcanzando el final de nuestro buffer, comprobamos si
// hay más información.
if (read == buffer.Length)
{

int nextByte = stream.ReadByte();

// Final del stream? Si es asi, esta hecho.
if (nextByte == -1)
{

return buffer;

}

// No. Redimensionamos el buffer, introducimos el byte
// que hemos leido y continuamos.
byte[] newBuffer = new byte[buffer.Length * 2];
Array.Copy(buffer, newBuffer, buffer.Length);
newBuffer[read] = (
byte)nextByte;
buffer = newBuffer;
read++;

}

}
// El buffer es ahora demasiado grande, lo encojemos.
byte[] ret = new byte[read];
Array.Copy(buffer, ret, read);
return ret;

}

Usando el código de arriba, no sé si síncronamente ó asíncronamente, no deberías sufrir los errores que pueden ocurrir haciéndolo de otras maneras, como datos corrompidos ó incompletos.

Aún no hay comentarios »

Aún no hay comentarios.

Canal RSS de los comentarios de la entrada. URI para TrackBack.

Deja un comentario

Tienes que iniciar sesión para escribir un comentario.

Blog de WordPress.com.