Удаление завершающих нулей из байтового массива в C # - PullRequest
17 голосов
/ 27 октября 2008

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

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

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

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

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

Ответы [ 10 ]

19 голосов
/ 27 октября 2008

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

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

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

10 голосов
/ 27 октября 2008

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

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

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

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

7 голосов
/ 27 октября 2008

@ Фактор Мистик,

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

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();
4 голосов
/ 27 октября 2008

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

[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
}
2 голосов
/ 27 октября 2008

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

1 голос
/ 07 февраля 2010

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

    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;
        }
0 голосов
/ 28 апреля 2010

В моем случае подход 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;
    }
0 голосов
/ 27 октября 2008

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

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

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

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;
0 голосов
/ 27 октября 2008

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

0 голосов
/ 27 октября 2008

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