Domanda

Ok, sto leggendo i file dat in un array di byte. Per qualche ragione, le persone che generano questi file mettono alla fine del file inutili byte nulli da mezzo megabyte. Qualcuno sa un modo rapido per tagliare questi alla fine?

Il primo pensiero è stato quello di iniziare alla fine dell'array e iterare all'indietro fino a quando ho trovato qualcosa di diverso da un null, quindi copiare tutto fino a quel punto, ma mi chiedo se non c'è un modo migliore.

Per rispondere ad alcune domande: Sei sicuro che gli 0 byte siano sicuramente nel file, piuttosto che ci sia un bug nel codice di lettura del file? Sì, ne sono certo.

Puoi sicuramente tagliare tutti gli 0 finali? Sì.

Possono esserci 0 nel resto del file? Sì, ci possono essere altri 0 posti, quindi no, non posso iniziare all'inizio e fermarmi al primo 0.

È stato utile?

Soluzione

Date le domande extra che hanno ricevuto una risposta, sembra che tu stia fondamentalmente facendo la cosa giusta. In particolare, devi toccare ogni byte del file dagli ultimi 0 in poi, per verificare che abbia solo 0s.

Ora, se devi copiare tutto o no dipende da cosa stai facendo con i dati.

  • Forse potresti ricordare l'indice e tenerlo con i dati o il nome del file.
  • È possibile copiare i dati in un nuovo array di byte
  • Se vuoi " riparare " il file, è possibile chiamare FileStream.SetLength per troncare il file

Il " tu hai per leggere ogni byte tra il punto di troncamento e la fine del file " è la parte critica però.

Altri suggerimenti

Sono d'accordo con Jon. Il punto critico è che devi " toccare " ogni byte dall'ultimo fino al primo byte diverso da zero. Qualcosa del genere:

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);

Sono abbastanza sicuro che sia efficiente quanto riuscirai a farcela.

@Factor Mystic,

Penso che ci sia un modo più breve:

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();

Che ne dici di questo:

[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
}

Supponendo 0 = null, questa è probabilmente la tua scommessa migliore ... come modifica minore, potresti voler usare Buffer.BlockCopy quando finalmente copi i dati utili ..

prova questo:

    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;
        }

C'è sempre una risposta 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();

Potresti semplicemente contare il numero di zero alla fine dell'array e usarlo al posto di .Length quando ripeterai l'array in seguito. Potresti incapsulare questo come preferisci. Il punto principale è che non è necessario copiarlo in una nuova struttura. Se sono grandi, può valerne la pena.

se nel file i byte nulli possono essere valori validi, sai che l'ultimo byte nel file non può essere nullo. in tal caso, iterare all'indietro e cercare la prima voce non nulla è probabilmente il migliore, altrimenti non c'è modo di dire dove si trova la fine effettiva del file.

Se sai di più sul formato dei dati, come ad esempio non può esserci una sequenza di byte null più lunga di due byte (o qualche vincolo simile). Quindi potresti effettivamente essere in grado di fare una ricerca binaria per il "punto di transizione". Questo dovrebbe essere molto più veloce della ricerca lineare (supponendo che tu possa leggere l'intero file).

L'idea di base (usando la mia ipotesi precedente su nessun byte nullo consecutivo) sarebbe:

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;

Nel mio caso l'approccio LINQ non è mai finito ^))) È lento nel lavorare con array di byte!

Ragazzi, perché non userete il metodo 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;
    }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top