getbytes - byte[] c# example
¿Cómo obtener little endian data de big endian en c#usando el método bitConverter.ToInt32? (8)
Agregue una referencia a System.Memory nuget y use BinaryPrimitives.ReverseEndianness ().
using System.Buffers.Binary;
number = BinaryPrimitives.ReverseEndianness(number);
Admite enteros con signo y sin signo (byte / short / int / long).
Estoy haciendo la aplicación en c #. En esa aplicación tengo una matriz de bytes que contiene valores hexadecimales.
Aquí estoy obteniendo datos como un gran endian, pero lo quiero como un pequeño endian.
Aquí estoy usando el método Bitconverter.toInt32
para convertir ese valor a entero.
Pero mi problema es que antes de convertir el valor, tengo que copiar esos datos de 4 bytes en una matriz temporal de la matriz de bytes de origen y luego revertir esa matriz de bytes temporal.
No puedo revertir la matriz de origen porque contiene otros datos también.
Por eso mi aplicación se vuelve lenta. código Aquí tengo una matriz de origen de bytes como waveData []. Contiene gran cantidad de datos.
byte[] tempForTimestamp=new byte[4];
tempForTimestamp[0] = waveData[290];
tempForTimestamp[1] = waveData[289];
tempForTimestamp[2] = waveData[288];
tempForTimestamp[3] = waveData[287];
int number = BitConverter.ToInt32(tempForTimestamp, 0);
¿Hay algún otro método para esa conversión?
Aqui tienes
public static int SwapEndianness(int value)
{
var b1 = (value >> 0) & 0xff;
var b2 = (value >> 8) & 0xff;
var b3 = (value >> 16) & 0xff;
var b4 = (value >> 24) & 0xff;
return b1 << 24 | b2 << 16 | b3 << 8 | b4 << 0;
}
Declara esta clase:
using static System.Net.IPAddress;
namespace BigEndianExtension
{
public static class BigEndian
{
public static short ToBigEndian(this short value) => HostToNetworkOrder(value);
public static int ToBigEndian(this int value) => HostToNetworkOrder(value);
public static long ToBigEndian(this long value) => HostToNetworkOrder(value);
public static short FromBigEndian(this short value) => NetworkToHostOrder(value);
public static int FromBigEndian(this int value) => NetworkToHostOrder(value);
public static long FromBigEndian(this long value) => NetworkToHostOrder(value);
}
}
Ejemplo, cree un formulario con un botón y un cuadro de texto multilínea:
using BigEndianExtension;
private void button1_Click(object sender, EventArgs e)
{
short int16 = 0x1234;
int int32 = 0x12345678;
long int64 = 0x123456789abcdef0;
string text = string.Format("LE:{0:X4}/r/nBE:{1:X4}/r/n", int16, int16.ToBigEndian());
text += string.Format("LE:{0:X8}/r/nBE:{1:X8}/r/n", int32, int32.ToBigEndian());
text += string.Format("LE:{0:X16}/r/nBE:{1:X16}/r/n", int64, int64.ToBigEndian());
textBox1.Text = text;
}
//Some code...
En la versión moderna de Linq, la versión más sencilla y fácil de entender sería:
int number = BitConverter.ToInt32(waveData.Skip(286).Take(4).Reverse().ToArray(), 0);
También podrías ...
byte[] tempForTimestamp = new byte[4];
Array.Copy(waveData, 287, tempForTimestamp, 0, 4);
Array.Reverse(tempForTimestamp);
int number = BitConverter.ToInt32(tempForTimestamp);
:)
No me gusta BitConverter
, porque (como contestó Marc Gravell) se especifica que se basa en el endianness del sistema, lo que significa que técnicamente tiene que hacer una verificación del endianness del sistema cada vez que use BitConverter
para asegurarse de que no tiene que revertir la matriz. Y generalmente, con los archivos guardados, generalmente se sabe la endianness que se intenta leer, y eso podría no ser lo mismo. Es posible que también esté manejando formatos de archivo con valores big-endian, como, por ejemplo, fragmentos PNG.
Por eso, solo escribí mis propios métodos para esto, que toman una matriz de bytes, el desplazamiento de lectura y la longitud de lectura como argumentos, así como un booleano para especificar el manejo del endianness, y que utiliza el cambio de bits para mayor eficiencia:
public static UInt64 ReadIntFromByteArray(Byte[] data, Int32 startIndex, Int32 bytes, Boolean littleEndian)
{
Int32 lastByte = bytes - 1;
if (data.Length < startIndex + bytes)
throw new ArgumentOutOfRangeException("startIndex", "Data array is too small to read a " + bytes + "-byte value at offset " + startIndex + ".");
UInt64 value = 0;
for (Int32 index = 0; index < bytes; index++)
{
Int32 offs = startIndex + (littleEndian ? index : lastByte - index);
value += (UInt64)(data[offs] << (8 * index));
}
return value;
}
Este código puede manejar cualquier valor entre 1 y 8 bytes, tanto little-endian como big-endian. La única peculiaridad de uso pequeño es que necesita dar tanto la cantidad de bytes para leer, como la necesidad de convertir específicamente el resultado al tipo que desee.
Ejemplo de un código donde lo usé para leer el encabezado de algún tipo de imagen de propietario:
Int16 imageWidth = (Int16) ReadIntFromByteArray(fileData, hdrOffset, 2, true);
Int16 imageHeight = (Int16) ReadIntFromByteArray(fileData, hdrOffset + 2, 2, true);
Esto leerá dos enteros consecutivos de 16 bits de una matriz, como valores little-endian firmados. Por supuesto, puedes hacer un montón de funciones de sobrecarga para todas las posibilidades, como esta:
public Int16 ReadInt16FromByteArrayLe(Byte[] data, Int32 startIndex)
{
return (Int16) ReadIntFromByteArray(data, startIndex, 2, true);
}
Pero personalmente no me molesté en eso.
Y, aquí es lo mismo para escribir bytes:
public static void WriteIntToByteArray(Byte[] data, Int32 startIndex, Int32 bytes, Boolean littleEndian, UInt64 value)
{
Int32 lastByte = bytes - 1;
if (data.Length < startIndex + bytes)
throw new ArgumentOutOfRangeException("startIndex", "Data array is too small to write a " + bytes + "-byte value at offset " + startIndex + ".");
for (Int32 index = 0; index < bytes; index++)
{
Int32 offs = startIndex + (littleEndian ? index : lastByte - index);
data[offs] = (Byte) (value >> (8*index) & 0xFF);
}
}
El único requisito aquí es que debe convertir el argumento arg de entrada a un entero sin signo de 64 bits al pasarlo a la función.
Si no volverá a necesitar esa matriz temporal invertida, puede crearla al pasar el parámetro, en lugar de hacer cuatro asignaciones. Por ejemplo:
int i = 287;
int value = BitConverter.ToInt32({
waveData(i + 3),
waveData(i + 2),
waveData(i + 1),
waveData(i)
}, 0);
Si sabe que los datos son big-endian, quizás solo hágalo manualmente:
int value = (buffer[i++] << 24) | (buffer[i++] << 16)
| (buffer[i++] << 8) | buffer[i++];
Esto funcionará de manera confiable en cualquier CPU, también. Nota i
es su desplazamiento actual en el búfer.
Otro enfoque sería mezclar la matriz:
byte tmp = buffer[i+3];
buffer[i+3] = buffer[i];
buffer[i] = tmp;
tmp = buffer[i+2];
buffer[i+2] = buffer[i+1];
buffer[i+1] = tmp;
int value = BitConverter.ToInt32(buffer, i);
i += 4;
Encuentro el primero mucho más legible, y no hay ramas / código complejo, por lo que también debería funcionar bastante rápido. El segundo también podría tener problemas en algunas plataformas (donde la CPU ya está ejecutando big-endian).
Uso las siguientes funciones de ayuda.
public static Int16 ToInt16(byte[] data, int offset)
{
if (BitConverter.IsLittleEndian)
return BitConverter.ToInt16(BitConverter.IsLittleEndian ? data.Skip(offset).Take(2).Reverse().ToArray() : data, 0);
return BitConverter.ToInt16(data, offset);
}
public static Int32 ToInt32(byte[] data, int offset)
{
if (BitConverter.IsLittleEndian)
return BitConverter.ToInt32(BitConverter.IsLittleEndian ? data.Skip(offset).Take(4).Reverse().ToArray() : data, 0);
return BitConverter.ToInt32(data, offset);
}
public static Int64 ToInt64(byte[] data, int offset)
{
if (BitConverter.IsLittleEndian)
return BitConverter.ToInt64(BitConverter.IsLittleEndian ? data.Skip(offset).Take(8).Reverse().ToArray() : data, 0);
return BitConverter.ToInt64(data, offset);
}