3

バックグラウンド:

私はC#にかなり慣れておらず、現在、主に学習を目的としたプロジェクトに取り組んでいます。QuakeとQuakeIIで使用されているように、.PAKファイルを処理するライブラリを作成しています。空白のアーカイブを書き込むだけでなく、それらを正常に読み取って抽出することができ、アーカイブにデータを書き込むことができます。

基本的な仕様は、参照用にここにあります:http: //debian.fmi.uni-sofia.bg/~sergei/cgsr/docs/pak.txt

問題:

バイナリデータ部分の最後までシークして、指定されたファイルからデータを挿入したいと思います。空のファイルを作成するときにメソッドでBinaryWriterを使用するので、これを挿入に使用しようとしています。私が見つけたものから、BinaryWriterでシークして書き込むと、指定された位置に書き込み、既存のデータが上書きされます。

私の目標は、既存のデータを失うことなく、特定のポイントにデータを挿入することです。これが不可能な場合は、個々のファイル情報(名前、オフセット、長さ)を含むインデックス全体を抽出し、バイナリデータを追加してから、インデックスを追加し、書き込んでいるファイルの情報を追加する必要があると思います。

何か考えや提案はありますか?

4

4 に答える 4

4

C#IOクラスを使用してファイルにデータを直接挿入することはできません。最も簡単な方法は、バッファに読み込んでバッファを操作するか、最初の部分を一時ファイルに書き込んでから新しいデータを挿入し、ファイルの残りの部分を書き込んで名前を変更することです。または、ファイルの最後にデータを追加し、すべてを上に移動して、データを挿入する適切な量の使用可能なスペースを作成することもできます。

http://www.codeproject.com/Articles/17716/Insert-Text-into-Existing-Files-in-C-Without-Tempをご覧ください

于 2012-05-21T16:51:24.113 に答える
3

ストリームベースのオブジェクト(FileStreamなど)を直接使用することは、ファイルレベルの操作に適したオプションです。

途中のデータをファイルに挿入する方法はありません。ファイル全体を読み取って新しく挿入した部分で保存するか、挿入ポイントまで新しいファイルにコピーして新しいデータを追加し、残りをコピーする必要があります(この時点で、元のファイルを削除するか、新しい名前に変更します)。

于 2012-05-21T16:35:04.387 に答える
2

効率的に挿入するのは難しいです。特に、同じストリームに複数の挿入を行いたい場合。Stream自分が行った挿入を追跡し、実際には挿入されていないときに変更を挿入したふりをする独自のクラスを作成できます。たとえば、A位置10があり、その位置にを挿入する場合ですX。クラスは、ストリームに挿入を書き込んで後続のすべてのバイトを移動する代わりに、X位置10を要求するたびにA、位置11を要求するときに基になるストリームから戻ることができます(基になるストリームでは、位置10のままです)。それからあなたが電話するときFlushまたはCloseストリームでは、変更されたストリーム全体が、基になるストリームに一度にフラッシュされます。これを正しく行うには、非常に注意深いエンジニアリングが必要です。挿入に挿入したり、任意の順序で挿入したりする可能性があるためです。

これは、ストリームに任意のバイトを挿入し、巧妙なキャッシュなしで後続のすべてのバイトを移動する、私が作成したコードです。残念ながら、それはを使用しませんが、コーディングが簡単だったため、(シーク可能である必要があります)にBinaryWriter直接機能します。Streamストリームの現在の位置に挿入され、その後、挿入されたバイトの直後の位置になります。Encoding文字列を挿入するには、クラスを使用して文字列をバイトに変換します。

public static class StreamExtensions
{
    /// <summary>
    /// Inserts the specified binary data at the current position in the stream.
    /// </summary>
    /// <param name="stream">The stream.</param>
    /// <param name="array">The array.</param>
    /// <remarks>
    /// <para>Note that this is a very expensive operation, as all the data following the insertion point has to
    /// be moved.</para>
    /// <para>When this method returns, the writer will be positioned at the end of the inserted data.</para>
    /// </remarks>
    public static void Insert(this Stream stream, byte[] array)
    {
        #region Contract
        if (stream == null)
            throw new ArgumentNullException("writer");
        if (array == null)
            throw new ArgumentNullException("array");
        if (!stream.CanRead || !stream.CanWrite || !stream.CanSeek)
            throw new ArgumentException("writer");
        #endregion

        long originalPosition = stream.Position;
        long readingPosition = originalPosition;
        long writingPosition = originalPosition;

        int length = array.Length;
        int bufferSize = 4096;
        while (bufferSize < length)
            bufferSize *= 2;

        int currentBuffer = 1;
        int[] bufferContents = new int[2];
        byte[][] buffers = new [] { new byte[bufferSize], new byte[bufferSize] };
        Array.Copy(array, buffers[1 - currentBuffer], array.Length);
        bufferContents[1 - currentBuffer] = array.Length;

        bool done;

        do
        {
            bufferContents[currentBuffer] = ReadBlock(stream, ref readingPosition, buffers[currentBuffer], out done);

            // Switch buffers.
            currentBuffer = 1 - currentBuffer;

            WriteBlock(stream, ref writingPosition, buffers[currentBuffer], bufferContents[currentBuffer]);

        } while (!done);

        // Switch buffers.
        currentBuffer = 1 - currentBuffer;

        // Write the remaining data.
        WriteBlock(stream, ref writingPosition, buffers[currentBuffer], bufferContents[currentBuffer]);

        stream.Position = originalPosition + length;
    }

    private static void WriteBlock(Stream stream, ref long writingPosition, byte[] buffer, int length)
    {
        stream.Position = writingPosition;
        stream.Write(buffer, 0, length);
        writingPosition += length;
    }

    private static int ReadBlock(Stream stream, ref long readingPosition, byte[] buffer, out bool done)
    {
        stream.Position = readingPosition;
        int bufferContent = 0;
        int read;
        do
        {
            read = stream.Read(buffer, bufferContent, buffer.Length - bufferContent);
            bufferContent += read;
            readingPosition += read;
        } while (read > 0 && read < buffer.Length);
        done = read == 0;
        return bufferContent;
    }
}

このメソッドを使用するには、コードをコードと同じ名前空間に貼り付けてから、次のようにします。

Stream someStream = ...;
byte[] dataToInsert = new byte[]{ 0xDE, 0xAD, 0xBE, 0xEF };
someStream.Position = 5;
someStream.Insert(dataToInsert);

空のファイルを作成するためにそれを使用したため、あなたが持っていると言うのでBinaryWriter、このクラスで作業できるようにするには、実際のが必要になりますStream。代わりに静的File.Create ()メソッドとFile.Open()メソッドを使用でき、それらはオブジェクトを返しFileStreamます。後でまだ必要であると判断した場合は、コンストラクターBinaryWriterにストリームを提供することを使用できます。BinaryWriterライターからストリームを取得するには、BinaryWriter.BaseStreamを使用します。MiscUtilライブラリのを使用しBinaryWriterてそれを防止しない限り、aを閉じると基になるストリームも閉じることに注意してください。NonClosingStreamWrapper

于 2012-05-21T18:00:03.127 に答える
0

はい、あなたが言ったように、ファイルにデータを「挿入」することはできません。挿入ポイントまでリーダーとライターの組み合わせを使用して、新しいデータを書き込む必要があります。

.pak形式はわかりませんが、この例で説明できると思います。

    var sourceStream = System.IO.File.OpenRead("myFile.pak");
    var destStream = System.IO.File.Create("modified.pak");

    using (var reader = new System.IO.BinaryReader(sourceStream))
    {
        using (var writer = new System.IO.BinaryWriter(destStream))
        {
            while (reader.BaseStream.CanRead)
            {
                char data = reader.ReadChar();
                if (data == '?')
                {
                    writer.Write("<Found!>");
                }
                else
                {
                    writer.Write(data);
                }
            }
        }
    }
于 2012-05-21T16:52:46.930 に答える