Pergunta

Ok, eu estou lendo em dat arquivos em um array de bytes. Por alguma razão, as pessoas que geram esses arquivos colocar sobre o valor de meia meg de null inútil bytes no final do arquivo. Alguém sabe uma maneira rápida de cortar estes fora da final?

Primeiro pensamento foi para começar no final da matriz e repita para trás até que eu encontrei algo diferente de um nulo, então copiar tudo até aquele ponto, mas me pergunto se não há uma maneira melhor.

Para responder a algumas perguntas: Tem certeza que o 0 bytes são definitivamente no arquivo, em vez de haver um bug no código de leitura de arquivo? Sim, estou certo disso.

Você pode definitivamente cortar todos os 0s à direita? Sim.

Pode haver nenhum 0s no resto do arquivo? Sim, pode haver 0 de outros lugares, por isso, não, eu não posso começar no início e parada na primeira 0.

Foi útil?

Solução

Dadas as questões extra agora respondidas, parece que você está fazendo basicamente a coisa certa. Em particular, você tem que tocar cada byte do arquivo a partir do último 0 em diante, para verificar se ele só tem 0s.

Agora, se você tem que copiar tudo ou não, depende do que você está depois fazer com os dados.

  • Você poderia talvez lembre-se o índice e mantê-lo com os dados ou nome de arquivo.
  • Você pode copiar os dados para uma nova matriz de bytes
  • Se você quiser "correção" do arquivo, você poderia chamar FileStream.SetLength para truncar o arquivo

O "você Have para ler cada byte entre o ponto de truncamento e o final do arquivo" é a parte crítica embora.

Outras dicas

Eu concordo com Jon. O bit importante é que você deve "tocar" cada byte do último até o primeiro byte não-zero. Algo parecido com isto:

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

Eu tenho certeza que é quase tão eficiente quanto você vai ser capaz de fazê-lo.

@Factor Mystic,

Eu acho que há um caminho mais curto:

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

Como sobre isto:

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

Assumindo 0 = null, que é provavelmente a sua melhor aposta ... como um menor tweak, você pode querer usar Buffer.BlockCopy quando você finalmente copiar os dados úteis ..

teste isso:

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

Há sempre uma resposta 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();

Você pode apenas contar o número de zero no final da matriz e da utilização que em vez de .Length quando a iteração a matriz mais tarde. Você pode encapsular este no entanto você gosta. ponto principal é que você realmente não precisa copiá-lo em uma nova estrutura. Se eles são grandes, pode valer a pena.

Se no arquivo bytes nulos podem ser valores válidos, você sabe que o último byte no arquivo não pode ser nulo. em caso afirmativo, iteração para trás e olhar para a primeira entrada não nulo é provavelmente melhor, se não, então não há nenhuma maneira de dizer onde o fim real do arquivo é.

Se você sabe mais sobre o formato de dados, como não pode haver seqüência de bytes nulos mais de dois bytes (ou alguma restrição similar). Então você pode ser capaz de realmente fazer uma pesquisa binária para o 'ponto de transição'. Isso deve ser muito mais rápido do que a busca linear (supondo que você pode ler em todo o arquivo).

A idéia básica (usando minha suposição anteriormente sobre não nulo consecutivo bytes), seria:

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;

Na minha abordagem LINQ caso nunca terminou ^))) É lento para trabalhar com matrizes de bytes!

Gente, por que você não vai usar Array.Copy () Método?

    /// <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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top