2008-10-27 3 views
16

Хорошо, я читаю в dat-файлах в массив байтов. По какой-то причине люди, которые генерируют эти файлы, помещают в конец файла бесполезные нулевые байты на половину мегабайта. Кто-нибудь знает быстрый способ обрезать их до конца?Удаление конечных нулей из байтового массива в C#

Сначала мысль заключалась в том, чтобы начать в конце массива и прокрутить назад, пока не найду что-то другое, кроме нулевого, а затем скопируйте все до этой точки, но мне интересно, нет ли лучшего способа.

Чтобы ответить на некоторые вопросы: Вы уверены, что 0 байт определенно в файле, а не ошибка в коде чтения файла? Да, я уверен в этом.

Можете ли вы определенно обрезать все задние 0s? Да.

Может ли быть остальная часть 0s в остальной части файла? Да, могут быть 0 других мест, поэтому нет, я не могу начинать с начала и останавливаться при первом 0.

ответ

9

Учитывая дополнительные вопросы, которые теперь отвечают, это звучит так, будто вы принципиально поступаете правильно. В частности, вам нужно прикоснуться к каждому байту файла с последнего 0, чтобы проверить, что он имеет только 0.

Теперь, нужно ли копировать все или нет, зависит от того, что вы делаете с данными.

  • Возможно, вы помните индекс и сохраните его с данными или именем файла.
  • Вы можете копировать данные в новый массив байтов
  • Если вы хотите, чтобы «исправить» файл, вы могли бы назвать FileStream.SetLength к обрезает файл

«ты есть читать каждый байт между точкой усечения и концом файла "является важной частью.

1

Предполагая, что 0 = нуль, это, вероятно, лучший выбор ... в качестве незначительной настройки , вы можете захотеть использовать Buffer.BlockCopy когда вы, наконец, скопируйте полезные данные ..

2

Как об этом:

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

Рассмотрение его как текста кажется рискованным - плюс вы только утроили File IO. – 2008-10-27 15:37:46

+0

О, и значительно увеличить CPU и т. Д. (Требуется время для кодирования/декодирования, даже для ASCII) – 2008-10-27 15:43:40

+0

Кодировка была только для теста ... для записи файла образца. Однако рассмотрение файла в виде текста может быть проблемой. – Rob 2008-10-27 15:49:10

0

Существует всегда 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(); 
+0

Я опубликовал более короткую альтернативу LINQ в отдельном ответе. Надеюсь, вам все это понравится. – 2008-10-27 18:20:48

+1

Если это большой буфер, было бы гораздо эффективнее просто использовать индексатор назад. Reverse() - операция буферизации и имеет стоимость выполнения. – 2008-10-27 22:15:11

0

вы можете просто посчитать число ноль в конце массива и использовать это вместо .Length при повторной итерации массива позже. Вы можете инкапсулировать это, как хотите. Главное, вам не нужно копировать его в новую структуру. Если они большие, это может стоить того.

16

Я согласен с Джоном. Критический бит заключается в том, что вы должны «касаться» каждого байта от последнего до первого ненулевого байта. Что-то вроде этого:

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

Я уверен, что это примерно так же эффективно, как вы собираетесь быть в состоянии сделать это.

6

@Factor Mystic,

Я думаю, что есть короткий путь:

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

если в файле нулевых байты могут быть допустимыми значениями, знаете ли вы, что последние байты в файле не могут быть ноль. если это так, итерация назад и поиск первой непустой записи, вероятно, лучше всего, если нет, то нет способа определить, где находится фактический конец файла.

Если вы знаете больше о формате данных, например, не может быть последовательности нулевых байтов длиной более двух байтов (или какого-либо подобного ограничения). Тогда вы можете фактически выполнить двоичный поиск «точки перехода». Это должно быть намного быстрее, чем линейный поиск (при условии, что вы можете читать во всем файле).

Основная идея (используя мое прежнее предположение о каких последовательных нулевых байтах), будет:

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

тест это:

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

В моем случае LINQ подход никогда не заканчивал ^))) Это медленнее работать с байтовыми массивами!

Ребята, почему вы не используете метод 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; 
    }