Удаление конечных нулей из массива байтов в C#

StackOverflow https://stackoverflow.com/questions/240258

  •  04-07-2019
  •  | 
  •  

Вопрос

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

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

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

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

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

Это было полезно?

Решение

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

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

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

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

Другие советы

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

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

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

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

Как насчет этого?

[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 = ноль, это, вероятно, ваша лучшая ставка ... в качестве незначительной настройки, вы можете использовать Buffer.BlockCopy , когда, наконец, скопируете полезные данные ..

проверить это

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

Всегда есть ответ 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();

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

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

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

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

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;

В моем случае подход 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;
    }
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top