2011-12-23 15 views
21

Estoy tratando de mejorar mi comprensión del formato de archivo STFS utilizando un programa para leer todos los diferentes bits de información. Usando un sitio web con una referencia de qué compensaciones contienen qué información, escribí un código que tiene un lector binario que pasa por el archivo y coloca los valores en las variables correctas.C# - Lector binario en Big Endian?

El problema es que se supone que todos los datos son Big Endian, y todo lo que lee el lector binario es Little Endian. Entonces, ¿cuál es la mejor manera de arreglar esto?

¿Puedo crear una clase mímica de lector binario que devuelva una matriz invertida de bytes? ¿Hay algo que pueda cambiar en la instancia de clase que lo haga leer en big endian para que no tenga que volver a escribir todo?

Cualquier ayuda es apreciada.

corregir: Intenté agregar Encoding.BigEndianUnicode como parámetro, pero todavía lee little endian.

+4

Skeet escribió uno: http: // www.yoda.arachsys.com/csharp/miscutil/ –

+0

@HansPassant, ¿Sería este uno de esos dlls que requieren que mi código sea de código abierto? ¿Por qué algunos dlls requieren eso? – mowwwalker

+0

Walkerneo borré mi respuesta porque zmbq respondió esencialmente lo mismo 3 minutos antes que yo. El concepto de endianness no se aplica a las matrices de bytes, solo a las palabras, dwords, qwords, etc., que son para grupos de 2, 4, 8 y así sucesivamente. Lo siento si significaría cambiar un montón de código, pero un hombre tiene que hacer lo que un hombre tiene que hacer. –

Respuesta

31

No soy normalmente uno para responder a mis propias preguntas, pero yo he logrado exactamente lo que quería con algún código simple:

class BinaryReader2 : BinaryReader { 
    public BinaryReader2(System.IO.Stream stream) : base(stream) { } 

    public override int ReadInt32() 
    { 
     var data = base.ReadBytes(4); 
     Array.Reverse(data); 
     return BitConverter.ToInt32(data, 0); 
    } 

    public Int16 ReadInt16() 
    { 
     var data = base.ReadBytes(2); 
     Array.Reverse(data); 
     return BitConverter.ToInt16(data, 0); 
    } 

    public Int64 ReadInt64() 
    { 
     var data = base.ReadBytes(8); 
     Array.Reverse(data); 
     return BitConverter.ToInt64(data, 0); 
    } 

    public UInt32 ReadUInt32() 
    { 
     var data = base.ReadBytes(4); 
     Array.Reverse(data); 
     return BitConverter.ToUInt32(data, 0); 
    } 

} 

sabía que es lo Yo quería, pero no sabía cómo escribirlo. Encontré esta página y me ayudó: http://www.codekeep.net/snippets/870c4ab3-419b-4dd2-a950-6d45beaf1295.aspx

+12

Fuera de tema, pero los campos de su clase ('a16' etc.) son innecesarios. Les asigna una matriz durante la construcción, pero dentro de cada método reemplaza esa matriz con una nueva matriz devuelta por la función 'Leer'. Simplemente podría poner 'var a32 = base.ReadBytes ...' en cada método y deshacerse de los campos. –

+15

No son innecesarios, son dañinos. Convertir lo que es (potencialmente, ignorar el flujo compartido subyacente) un código seguro para subprocesos en una situación de estado compartido. – skolima

+4

Es probable que desee comprobar 'BitConverter.IsLittleEndian' antes de invertir. Si es 'falso', no necesita invertir. –

5

No estoy familiarizado con STFS, pero cambiar endianess es relativamente fácil. "Network Order" es Big Endian, por lo que todo lo que necesita hacer es traducir de la red al orden de host.

Esto es fácil porque ya hay un código que lo hace. Mira IPAddress.NetworkToHostOrder, como se explica aquí: ntohs() and ntohl() equivalent?

9

En mi humilde opinión, una respuesta un poco mejor, ya que no requiere una clase diferente para ser renovado, hace que las llamadas de Big-Endian sean obvias y permite llamadas grandes y poco endia estar mezclado en la corriente.

public static class Helpers 
{ 
    // Note this MODIFIES THE GIVEN ARRAY then returns a reference to the modified array. 
    public static byte[] Reverse(this byte[] b) 
    { 
    Array.Reverse(b); 
    return b; 
    } 

    public static UInt16 ReadUInt16BE(this BinaryReader binRdr) 
    { 
    return BitConverter.ToUInt16(binRdr.ReadBytesRequired(sizeof(UInt16)).Reverse(), 0); 
    } 

    public static Int16 ReadInt16BE(this BinaryReader binRdr) 
    { 
    return BitConverter.ToInt16(binRdr.ReadBytesRequired(sizeof(Int16)).Reverse(), 0); 
    } 

    public static UInt32 ReadUInt32BE(this BinaryReader binRdr) 
    { 
    return BitConverter.ToUInt32(binRdr.ReadBytesRequired(sizeof(UInt32)).Reverse(), 0); 
    } 

    public static Int32 ReadInt32BE(this BinaryReader binRdr) 
    { 
    return BitConverter.ToInt32(binRdr.ReadBytesRequired(sizeof(Int32)).Reverse(), 0); 
    } 

    public static byte[] ReadBytesRequired(this BinaryReader binRdr, int byteCount) 
    { 
    var result = binRdr.ReadBytes(byteCount); 

    if (result.Length != byteCount) 
     throw new EndOfStreamException(string.Format("{0} bytes required from stream, but only {1} returned.", byteCount, result.Length)); 

    return result; 
    } 
} 
+7

Recuerde comprobar 'BitConverter.IsLittleEndian' antes de invertir. –

4

En mi opinión, debe tener cuidado al hacer esto. La razón por la que uno querría Convertir de BigEndian a LittleEndian es si los bytes que se leen están en BigEndian y el sistema operativo que calcula contra ellos está funcionando en LittleEndian.

C# ya no es un lenguaje de solo ventana. Con puertos como Mono, y también otras plataformas de Microsoft como Windows Phone 7/8, Xbox 360/Xbox One, Windwos CE, Windows 8 Mobile, Linux con MONO, Apple con MONO, etc. Es bastante posible que la plataforma operativa pueda estar en BigEndian, en cuyo caso te enfadarías si convirtieras el código sin hacer ningún control.

El BitConverter ya tiene un campo llamado "IsLittleEndian" que puede usar para determinar si el entorno operativo está en LittleEndian o no. Entonces puedes hacer la reversión condicionalmente.

Como tal, que en realidad sólo escribió algunas [] extensiones de bytes en lugar de hacer una clase grande:

/// <summary> 
    /// Get's a byte array from a point in a source byte array and reverses the bytes. Note, if the current platform is not in LittleEndian the input array is assumed to be BigEndian and the bytes are not returned in reverse order 
    /// </summary> 
    /// <param name="byteArray">The source array to get reversed bytes for</param> 
    /// <param name="startIndex">The index in the source array at which to begin the reverse</param> 
    /// <param name="count">The number of bytes to reverse</param> 
    /// <returns>A new array containing the reversed bytes, or a sub set of the array not reversed.</returns> 
    public static byte[] ReverseForBigEndian(this byte[] byteArray, int startIndex, int count) 
    { 
     if (BitConverter.IsLittleEndian) 
      return byteArray.Reverse(startIndex, count); 
     else 
      return byteArray.SubArray(startIndex, count); 

    } 

    public static byte[] Reverse(this byte[] byteArray, int startIndex, int count) 
    { 
     byte[] ret = new byte[count]; 
     for (int i = startIndex + (count - 1); i >= startIndex; --i) 
     { 
      byte b = byteArray[i]; 
      ret[(startIndex + (count - 1)) - i] = b; 
     } 
     return ret; 
    } 

    public static byte[] SubArray(this byte[] byteArray, int startIndex, int count) 
    { 
     byte[] ret = new byte[count]; 
     for (int i = 0; i < count; ++i)    
      ret[0] = byteArray[i + startIndex]; 
     return ret; 
    } 

así que imagina este código de ejemplo:

byte[] fontBytes = byte[240000]; //some data loaded in here, E.G. a TTF TrueTypeCollection font file. (which is in BigEndian) 

int _ttcVersionMajor = BitConverter.ToUint16(fontBytes.ReverseForBigEndian(4, 2), 0); 

//output 
_ttcVersionMajor = 1 //TCCHeader is version 1 
Cuestiones relacionadas