web-dev-qa-db-ja.com

C#のバイト配列から末尾のnullを削除する

OK、データファイルをバイト配列に読み込んでいます。何らかの理由で、これらのファイルを生成する人々は、ファイルの最後に約0.5メガバイト相当の役に立たないヌルバイトを置きます。誰かがこれらを最後から取り除く簡単な方法を知っていますか?

最初に考えたのは、配列の最後から始めて、null以外のものが見つかるまで逆方向に繰り返し、その時点まですべてをコピーすることでしたが、もっと良い方法はないのではないかと思います。

いくつかの質問に答えるには:ファイル読み取りコードにバグがあるのではなく、0バイトが間違いなくファイルにあることを確認しますか?はい、私はそれを確信しています。

末尾の0をすべて確実にトリミングできますか?はい。

ファイルの残りの部分に0を含めることはできますか?はい、他に0の場所がある可能性があるので、いいえ、最初から始めて最初の0で停止することはできません。

17
Kevin

現在回答されている追加の質問を考えると、基本的に正しいことをしているように思えます。特に、ファイルの最後の0以降のすべてのバイトに触れて、ファイルに0しかないことを確認する必要があります。

さて、すべてをコピーする必要があるかどうかは、データをどのように処理しているかによって異なります。

  • おそらく、インデックスを覚えて、データまたはファイル名とともに保持することができます。
  • データを新しいバイト配列にコピーできます
  • ファイルを「修正」したい場合は、 FileStream.SetLength を呼び出してファイルを切り捨てることができます。

ただし、「切り捨てポイントからファイルの終わりまでのすべてのバイトを読み取る必要がある」は重要な部分です。

11
Jon Skeet

ジョンに同意します。重要なのは、最後のバイトから最初のゼロ以外のバイトまでのすべてのバイトに「触れる」必要があるということです。このようなもの:

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

私はそれがあなたがそれを作ることができるだろうとほぼ同じくらい効率的であるとかなり確信しています。

22
Coderer

@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();
8
Brian J Cardiff

これはどう:

[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
Rob

0 = nullと仮定すると、おそらくそれが最善の策です...マイナーな調整として、最終的に有用なデータをコピーするときにBuffer.BlockCopyを使用することをお勧めします。

2
Marc Gravell

これをテストします:

    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;
        }
1
A.Yaqin

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
Factor Mystic

ファイル内のnullバイトが有効な値である可能性がある場合、ファイルの最後のバイトをnullにすることはできないことをご存知ですか。もしそうなら、逆方向に繰り返し、最初のnull以外のエントリを探すのがおそらく最善です。そうでない場合、ファイルの実際の終わりがどこにあるかを知る方法はありません。

2バイトより長いnullバイトのシーケンス(または同様の制約)がないなど、データ形式について詳しく知っている場合。そうすれば、実際に「遷移点」の二分探索を行うことができるかもしれません。これは、線形検索よりもはるかに高速であるはずです(ファイル全体を読み取ることができると仮定します)。

基本的な考え方(連続するヌルバイトがないという以前の仮定を使用)は、次のようになります。

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
luke

配列の最後にあるゼロの数を数えて、後で配列を反復するときに.Lengthの代わりにそれを使用することができます。これは好きなようにカプセル化できます。重要な点は、それを新しい構造にコピーする必要がないということです。それらが大きい場合、それは価値があるかもしれません。

0
Greg Dean