Pregunta

Ok, estoy leyendo archivos de datos en una matriz de bytes. Por alguna razón, las personas que generan estos archivos ponen alrededor de medio megago de bytes nulos inútiles al final del archivo. ¿Alguien sabe una forma rápida de recortar esto del final?

Lo primero que pensé fue comenzar al final de la matriz e iterar hacia atrás hasta que encontré algo más que un nulo, luego copiar todo hasta ese punto, pero me pregunto si no hay una mejor manera.

Para responder algunas preguntas: ¿Está seguro de que los 0 bytes están definitivamente en el archivo, en lugar de que haya un error en el código de lectura del archivo? Sí, estoy seguro de eso.

¿Definitivamente puedes recortar todos los 0s finales? Si.

¿Puede haber algún 0 en el resto del archivo? Sí, puede haber otros lugares de 0, así que, no, no puedo comenzar desde el principio y detenerme en el primer 0.

¿Fue útil?

Solución

Teniendo en cuenta las preguntas adicionales que ahora se responden, parece que básicamente estás haciendo lo correcto. En particular, debe tocar cada byte del archivo desde el último 0 en adelante, para verificar que solo tenga 0s.

Ahora, si tiene que copiar todo o no, depende de lo que haga con los datos.

  • Quizás pueda recordar el índice y conservarlo con los datos o el nombre de archivo.
  • Puede copiar los datos en una nueva matriz de bytes
  • Si quieres " corregir " En el archivo, puede llamar a FileStream.SetLength para truncar el archivo

El " usted tiene para leer cada byte entre el punto de corte y el final del archivo " Sin embargo, es la parte crítica.

Otros consejos

Estoy de acuerdo con Jon. El bit crítico es que debes " tocar " cada byte desde el último hasta el primer byte distinto de cero. Algo como esto:

byte[] foo;
// populate foo
int i = foo.Length - 1;
while(foo[i] == 0)
    --i;
// now foo[i] is the last non-zero byte
byte[] bar = new byte[i+1];
Array.Copy(foo, bar, i+1);

Estoy bastante seguro de que es tan eficiente como podrás hacerlo.

@Factor Mystic,

Creo que hay un camino más corto:

var data = new byte[] { 0x01, 0x02, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00 };
var new_data = data.TakeWhile((v, index) => data.Skip(index).Any(w => w != 0x00)).ToArray();

¿Qué tal esto?

[Test]
public void Test()
{
   var chars = new [] {'a', 'b', '\0', 'c', '\0', '\0'};

   File.WriteAllBytes("test.dat", Encoding.ASCII.GetBytes(chars));

   var content = File.ReadAllText("test.dat");

   Assert.AreEqual(6, content.Length); // includes the null bytes at the end

   content = content.Trim('\0');

   Assert.AreEqual(4, content.Length); // no more null bytes at the end
                                       // but still has the one in the middle
}

Suponiendo que 0 = nulo, esa es probablemente su mejor apuesta ... como una modificación menor, es posible que desee utilizar Buffer.BlockCopy cuando finalmente copie los datos útiles ...

prueba esto:

    private byte[] trimByte(byte[] input)
    {
        if (input.Length > 1)
        {
            int byteCounter = input.Length - 1;
            while (input[byteCounter] == 0x00)
            {
                byteCounter--;
            }
            byte[] rv = new byte[(byteCounter + 1)];
            for (int byteCounter1 = 0; byteCounter1 < (byteCounter + 1); byteCounter1++)
            {
                rv[byteCounter1] = input[byteCounter1];
            }
            return rv;
        }

Siempre hay una respuesta LINQ

byte[] data = new byte[] { 0x01, 0x02, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00 };
bool data_found = false;
byte[] new_data = data.Reverse().SkipWhile(point =>
{
  if (data_found) return false;
  if (point == 0x00) return true; else { data_found = true; return false; }
}).Reverse().ToArray();

Podrías simplemente contar el número de cero al final de la matriz y usarlo en lugar de .Length cuando se repita la matriz más adelante. Podrías encapsular esto como quieras. El punto principal es que realmente no es necesario copiarlo en una nueva estructura. Si son grandes, puede valer la pena.

si en el archivo los bytes nulos pueden ser valores válidos, ¿sabe que el último byte del archivo no puede ser nulo? si es así, iterando hacia atrás y buscando la primera entrada que no sea nula es probablemente la mejor, si no, entonces no hay manera de decir dónde está el final real del archivo.

Si sabe más sobre el formato de los datos, por ejemplo, no puede haber una secuencia de bytes nulos de más de dos bytes (o alguna restricción similar). Entonces puede hacer una búsqueda binaria para el "punto de transición". Esto debería ser mucho más rápido que la búsqueda lineal (asumiendo que se puede leer en todo el archivo).

La idea básica (utilizando mi suposición anterior de que no hay bytes nulos consecutivos), sería:

var data = (byte array of file data...);
var index = data.length / 2;
var jmpsize = data.length/2;
while(true)
{
    jmpsize /= 2;//integer division
    if( jmpsize == 0) break;
    byte b1 = data[index];
    byte b2 = data[index + 1];
    if(b1 == 0 && b2 == 0) //too close to the end, go left
        index -=jmpsize;
    else
        index += jmpsize;
}

if(index == data.length - 1) return data.length;
byte b1 = data[index];
byte b2 = data[index + 1];
if(b2 == 0)
{
    if(b1 == 0) return index;
    else return index + 1;
}
else return index + 2;

En mi caso, el enfoque de LINQ nunca terminó ^))) ¡Es muy lento para trabajar con matrices de bytes!

Chicos, ¿por qué no usarán el método Array.Copy ()?

    /// <summary>
    /// Gets array of bytes from memory stream.
    /// </summary>
    /// <param name="stream">Memory stream.</param>
    public static byte[] GetAllBytes(this MemoryStream stream)
    {
        byte[] result = new byte[stream.Length];
        Array.Copy(stream.GetBuffer(), result, stream.Length);

        return result;
    }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top