17

好的,我正在将 dat 文件读入字节数组。出于某种原因,生成这些文件的人在文件末尾放置了大约半兆的无用空字节。有谁知道快速修剪这些的方法吗?

第一个想法是从数组的末尾开始并向后迭代,直到找到除 null 以外的其他内容,然后将所有内容复制到该点,但我想知道是否没有更好的方法。

回答一些问题:您确定文件中肯定存在 0 字节,而不是文件读取代码中存在错误吗?是的,我很确定。

你能确定修剪所有尾随的 0 吗?是的。

文件的其余部分可以有任何 0 吗?是的,其他地方可以有 0,所以,不,我不能从头开始,在第一个 0 处停止。

4

11 回答 11

23

我同意乔恩的观点。关键是您必须“触摸”从最后一个字节到第一个非零字节的每个字节。像这样的东西:

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

我很确定这和你能做到的一样有效。

于 2008-10-27T17:32:31.870 回答
11

鉴于现在回答了额外的问题,听起来你基本上做对了。特别是,您必须从最后一个 0 开始触摸文件的每个字节,以检查它是否只有 0。

现在,您是否必须复制所有内容取决于您对数据所做的操作。

  • 您也许可以记住索引并将其与数据或文件名一起保存。
  • 您可以将数据复制到一个新的字节数组中
  • 如果要“修复”文件,可以调用FileStream.SetLength来截断文件

“您必须读取截断点和文件末尾之间的每个字节”是关键部分。

于 2008-10-27T17:10:38.980 回答
9

@因子神秘主义者,

我认为有一个最短的方法:

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();
于 2008-10-27T17:33:50.243 回答
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
}
于 2008-10-27T15:30:50.210 回答
2

假设 0=null,这可能是您最好的选择...作为一个小调整,您可能希望Buffer.BlockCopy在最终复制有用数据时使用..

于 2008-10-27T15:23:50.070 回答
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;
        }
于 2010-02-07T07:24:03.510 回答
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();
于 2008-10-27T16:38:41.307 回答
0

您可以只计算数组末尾的零个数,然后在以后迭代数组时使用它而不是 .Length 。你可以随心所欲地封装它。要点是您实际上并不需要将其复制到新结构中。如果它们很大,它可能是值得的。

于 2008-10-27T16:56:01.133 回答
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;
于 2008-10-27T17:49:52.223 回答
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 类,可在此处找到。

而这个扩展方法:

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;
    }
}
于 2021-07-09T16:21:01.467 回答
-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;
    }
于 2010-04-27T22:02:08.130 回答