2008-10-27 95 views
16

好吧,我正在将dat文件读入一个字节数组。出于某种原因,生成这些文件的人在文件末尾放置了大约半个兆字节的无用空字节。任何人都知道快速修剪这些结尾的方法吗?从C中的字节数组中删除尾随的空值#

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

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

你绝对可以修剪所有尾部0吗?是。

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

回答

9

鉴于现在回答的额外问题,这听起来像是你从根本上做正确的事情。特别是,你必须触摸从最后0开始的文件的每个字节,以检查它只有0。

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

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

的“你读取每一个字节截断点和文件结尾之间“是关键部分。

1

假设0 = null,这可能是你最好的选择...作为一个小调整,你可能想使用Buffer.BlockCopy当你终于复制有用的数据..

2

如何:

[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

对待它作为文本似乎有风险 - 再加上你只是文件IO三倍。 – 2008-10-27 15:37:46

+0

哦,并且CPU等也增加了很多(编码/解码需要花费时间,即使对于ASCII) – 2008-10-27 15:43:40

+0

编码仅用于测试...编写示例文件。尽管将文件作为文本处理可能是个问题。 – Rob 2008-10-27 15:49:10

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(); 
+0

我已经在单独的答案中发布了一个较短的LINQ备选方案。希望你们都喜欢! – 2008-10-27 18:20:48

+1

如果这是一个很大的缓冲区,那么简单地向后使用索引器会更有效率。 Reverse()是一个缓冲操作,并且具有性能成本。 – 2008-10-27 22:15:11

0

你可以只算数组末尾的零的数量,并在稍后迭代数组时使用它来代替.Length。无论你喜欢,你都可以封装它。重点是你并不需要将它复制到一个新的结构中。如果它们很大,这可能是值得的。

16

我同意乔恩。关键的一点是,你必须“触摸”从最后一个字节到第一个非零字节的每个字节。这样的事情:

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

我很确定这就像您将能够做到这一点一样高效。

6

@Factor神秘,

我认为这是一个最简单的办法:

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