好的,我正在将dat文件读入一个字节数组。出于某种原因,生成这些文件的人在文件末尾放置了大约半毫克的无用空字节。有人知道一个快速的方法来削减这些吗?
首先想到的是从数组的末尾开始并向后迭代,直到找到除null之外的其他东西,然后将所有内容复制到那一点,但我想知道是否有更好的方法。
回答一些问题: 你确定0字节肯定在文件中,而不是文件读取代码中有错误吗?是的,我确信这一点。
你能肯定削减所有尾随的0吗?是。
文件的其余部分可以有任何0吗?是的,可能有0个其他地方,所以,不,我不能从头开始并在第0个停止。
答案 0 :(得分:19)
我同意乔恩。关键位是你必须“触摸”从最后一个字节到第一个非零字节的每个字节。像这样:
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);
我很确定这个效果和你能够做到的一样高效。
答案 1 :(得分:10)
鉴于现在回答的额外问题,听起来你从根本上做了正确的事情。特别是,您必须从最后0开始触摸文件的每个字节,以检查它是否只有0。
现在,您是否必须复制所有内容取决于您对数据执行的操作。
“你有读取截断点和文件末尾之间的每个字节”是关键部分。
答案 2 :(得分:7)
@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();
答案 3 :(得分:4)
这个怎么样:
[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
}
答案 4 :(得分:2)
假设0 = null,这可能是你最好的选择......作为一个小调整,当你最终复制有用的数据时,你可能想要使用Buffer.BlockCopy
。
答案 5 :(得分: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;
}
答案 6 :(得分: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();
答案 7 :(得分:0)
您可以只计算数组末尾的零数,并在稍后迭代数组时使用它而不是.Length。你可以随意封装它。重点是您不需要将其复制到新结构中。如果它们很大,那可能是值得的。
答案 8 :(得分: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;
答案 9 :(得分:0)
当文件很大(比我的 RAM 大得多)时,我用它来删除尾随的空值:
static void RemoveTrailingNulls(string inputFilename, string outputFilename)
{
int bufferSize = 100 * 1024 * 1024;
long totalTrailingNulls = 0;
byte[] emptyArray = new byte[bufferSize];
using (var inputFile = File.OpenRead(inputFilename))
using (var inputFileReversed = new ReverseStream(inputFile))
{
var buffer = new byte[bufferSize];
while (true)
{
var start = DateTime.Now;
var bytesRead = inputFileReversed.Read(buffer, 0, buffer.Length);
if (bytesRead == emptyArray.Length && Enumerable.SequenceEqual(emptyArray, buffer))
{
totalTrailingNulls += buffer.Length;
}
else
{
var nulls = buffer.Take(bytesRead).TakeWhile(b => b == 0).Count();
totalTrailingNulls += nulls;
if (nulls < bytesRead)
{
//found the last non-null byte
break;
}
}
var duration = DateTime.Now - start;
var mbPerSec = (bytesRead / (1024 * 1024D)) / duration.TotalSeconds;
Console.WriteLine($"{mbPerSec:N2} MB/seconds");
}
var lastNonNull = inputFile.Length - totalTrailingNulls;
using (var outputFile = File.Open(outputFilename, FileMode.Create, FileAccess.Write))
{
inputFile.Seek(0, SeekOrigin.Begin);
inputFile.CopyTo(outputFile, lastNonNull, bufferSize);
}
}
}
它使用了 ReverseStream 类,可以在 here 中找到。
还有这个扩展方法:
public static class Extensions
{
public static long CopyTo(this Stream input, Stream output, long count, int bufferSize)
{
byte[] buffer = new byte[bufferSize];
long totalRead = 0;
while (true)
{
if (count == 0) break;
int read = input.Read(buffer, 0, (int)Math.Min(bufferSize, count));
if (read == 0) break;
totalRead += read;
output.Write(buffer, 0, read);
count -= read;
}
return totalRead;
}
}
答案 10 :(得分:-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;
}