Удаление конечных нулей из массива байтов в C#
Вопрос
Хорошо, я читаю файлы 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;
}