Frage

Ich habe eine große Datei, wo ich an einer bestimmten Stelle bestimmte Zeichen einfügen muß. Was ist der einfachste Weg, in C # zu tun, ohne die gesamte Datei wieder neu zu schreiben.

War es hilfreich?

Lösung

Dateisysteme unterstützen keine Daten in der Mitte einer Datei „Einfügen“. Wenn Sie wirklich eine Notwendigkeit für eine Datei haben, die in einer sortierten Art und Weise geschrieben werden kann, empfehle ich Ihnen, eine eingebettete Datenbank suchen in verwenden.

Sie können einen Blick auf SQLite oder BerkeleyDB .

Dann wieder, könnte man mit einer Textdatei oder einem Legacy-Binärdatei arbeiten. In diesem Fall der einzige Möglichkeit ist, um die Datei, zumindest von der Einfügemarke bis zum Ende neu zu schreiben.

Ich würde auf der Filestream Klasse random I / O in C # zu tun.

Andere Tipps

Sie werden wahrscheinlich brauchen die Datei von dem Punkt neu zu schreiben, um die Änderungen am Ende einzufügen. Sie könnten am besten sein, immer bis zum Ende der Datei und benutzen Werkzeuge wie Art zu schreiben und grep die Daten aus in der gewünschten Reihenfolge zu erhalten. Ich gehe davon aus, Sie sprechen über eine Textdatei hier nicht eine binäre Datei.

Es gibt keine Möglichkeit Zeichen in eine Datei einfügen, ohne sie neu zu schreiben. Mit C # kann es mit jedem Stream-Klassen erfolgen. Wenn die Dateien sehr groß sind, würde ich empfehlen Sie GNU Core-Utils innerhalb von C # -Code zu verwenden. Sie sind die schnellsten. Früher habe ich sehr große Textdateien mit dem Kern utils zu handhaben (von Größen 4GB, 8GB oder mehr usw.). Befehle wie Kopf, Schwanz, geteilt, csplit, Katze, shuf, fetzte, uniq wirklich viel in Textmanipulation helfen.

Zum Beispiel, wenn Sie einige Zeichen in einer 2 GB-Datei setzen müssen, können Sie Split--b BYTECOUNT verwenden, legen Sie die ouptut in eine Datei, fügen Sie den neuen Text zu ihm, und den Rest des Inhalts erhalten und in der es. Dies sollte angeblich schneller als jede andere Art und Weise.

Hoffe, es funktioniert. Probieren Sie es aus.

Sie Random Access verwenden können, um bestimmte Stellen einer Datei zu schreiben, aber Sie werden es nicht im Textformat der Lage zu tun, werden Sie haben direkt mit Bytes arbeiten.

Wenn Sie wissen, die bestimmten Ort, an dem Sie die neuen Daten schreiben möchten, verwenden Sie die Binary Klasse:

using (BinaryWriter bw = new BinaryWriter (File.Open (strFile, FileMode.Open)))
{
    string strNewData = "this is some new data";
    byte[] byteNewData = new byte[strNewData.Length];

    // copy contents of string to byte array
    for (var i = 0; i < strNewData.Length; i++)
    {
        byteNewData[i] = Convert.ToByte (strNewData[i]);
    }

    // write new data to file
    bw.Seek (15, SeekOrigin.Begin);  // seek to position 15
    bw.Write (byteNewData, 0, byteNewData.Length);
}

Sie können einen Blick auf dieses Projekt: Win Data Inspector

Im Grunde genommen ist der Code wie folgt vor:

// this.Stream is the stream in which you insert data

{

long position = this.Stream.Position;

long length = this.Stream.Length;

MemoryStream ms = new MemoryStream();

this.Stream.Position = 0;

DIUtils.CopyStream(this.Stream, ms, position, progressCallback);

ms.Write(data, 0, data.Length);

this.Stream.Position = position;

DIUtils.CopyStream(this.Stream, ms, this.Stream.Length - position, progressCallback);

this.Stream = ms;

}

#region Delegates

public delegate void ProgressCallback(long position, long total);

#endregion

DIUtils.cs

public static void CopyStream(Stream input, Stream output, long length, DataInspector.ProgressCallback callback)
{
    long totalsize = input.Length;
    long byteswritten = 0;
    const int size = 32768;
    byte[] buffer = new byte[size];
    int read;
    int readlen = length < size ? (int)length : size;
    while (length > 0 && (read = input.Read(buffer, 0, readlen)) > 0)
    {
        output.Write(buffer, 0, read);
        byteswritten += read;
        length -= read;
        readlen = length < size ? (int)length : size;
        if (callback != null)
            callback(byteswritten, totalsize);
    }
}

Je nach Umfang des Projektes, können Sie jede Textzeile mit der Datei in einer Tabelle Datenstruktur einzufügen, um zu entscheiden. So ähnlich wie eine Datenbanktabelle , auf diese Weise Sie an einen bestimmten Ort zu einem bestimmten Zeitpunkt einfügen können, und müssen nicht lesen, ändern, und geben den gesamten Text jedes Mal Datei. Dies ist angesichts der Tatsache, dass Ihre Daten „riesig“, wie Sie es nennen. Sie würden immer noch die Datei neu erstellen, aber zumindest Sie eine skalierbare Lösung auf diese Weise erstellen.

Es kann „möglich“ sein, je nachdem, wie das Dateisystem speichert Dateien schnell einfügen (dh, fügen Sie zusätzlich) in der Mitte Bytes. Wenn sie aus der Ferne möglich ist, kann es nur möglich sein, so einen vollständigen Block zu einem Zeitpunkt zu tun, und nur von beiden tun geringe Änderung des Dateisystems selbst oder durch ein Dateisystem spezifische Schnittstelle.

Dateisysteme werden im Allgemeinen nicht für diesen Betrieb ausgelegt. Wenn Sie müssen Sie wirklich eine allgemeinere Datenbank benötigen, um schnell tun Einsätze.

Je nach Anwendung eines Mittelweg Ihrer Einsätze zusammen zu bündeln würde, so dass Sie nur ein Umschreiben der ehen Datei tun als zwanzig.

Sie werden immer die verbleibenden Bytes von der Einfügemarke neu schreiben müssen. Wenn dieser Punkt bei 0 ist, dann werden Sie die gesamte Datei neu schreiben. Wenn es 10 Bytes vor dem letzten Byte ist, dann werden Sie das letzte 10 Bytes neu schreiben.

Auf jeden Fall gibt es keine Funktion direkt unterstützen „Einfügen in Datei“. Aber der folgende Code kann es genau tun.

var sw = new Stopwatch();
var ab = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ";

// create
var fs = new FileStream(@"d:\test.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite, 262144, FileOptions.None);
sw.Restart();
fs.Seek(0, SeekOrigin.Begin);
for (var i = 0; i < 40000000; i++) fs.Write(ASCIIEncoding.ASCII.GetBytes(ab), 0, ab.Length);
sw.Stop();
Console.WriteLine("{0} ms", sw.Elapsed.TotalMilliseconds);
fs.Dispose();

// insert
fs = new FileStream(@"d:\test.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite, 262144, FileOptions.None);
sw.Restart();
byte[] b = new byte[262144];
long target = 10, offset = fs.Length - b.Length;
while (offset != 0)
{
    if (offset < 0)
    {
        offset = b.Length - target;
        b = new byte[offset];
    }
    fs.Position = offset; fs.Read(b, 0, b.Length);
    fs.Position = offset + target; fs.Write(b, 0, b.Length);
    offset -= b.Length;
}
fs.Position = target; fs.Write(ASCIIEncoding.ASCII.GetBytes(ab), 0, ab.Length);
sw.Stop();
Console.WriteLine("{0} ms", sw.Elapsed.TotalMilliseconds);

Um eine bessere Leistung für Datei IO zu gewinnen, spielen mit „magischen zwei angetriebenen Zahlen“ wie in dem obigen Code. Die Erstellung der Datei verwendet einen Puffer von 262144 Bytes (256 KB), die überhaupt nicht helfen. Der gleiche Puffer für das Einfügen funktioniert die „Performance Job“, wie Sie durch die Stoppuhr Ergebnisse sehen können, wenn Sie den Code ausführen. Ein Entwurf Test auf meinem PC ergab die folgenden Ergebnisse:

13628,8 ms für die Erstellung und 3597,0971 ms für die Insertion.

Beachten Sie, dass das Soll-Byte für die Insertion 10, was bedeutet, dass fast die gesamte Datei neu geschrieben wurde.

Warum Sie nicht über einen Zeiger auf das Ende der Datei gesetzt (wörtlich vier Bytes über die aktuelle Größe der Datei) und dann auf das Ende der Datei die Länge der eingefügten Daten schreiben, und schließlich die Daten, die Sie wollen sich einzufügen. Zum Beispiel, wenn Sie eine Zeichenfolge in der Mitte der Datei haben, und Sie mögen einige Zeichen in der Mitte der Zeichenfolge einzufügen, können Sie einen Zeiger auf das Ende der Datei über etwa vier Zeichen in der Zeichenfolge, schreiben und dann schreiben dass vier Zeichen bis zum Ende zusammen mit den Zeichen, die Sie zunächst einfügen wollten. Es geht nur um Daten zu bestellen. Natürlich können Sie dies nur tun können, wenn Sie selbst die ganze Datei schreiben, meine ich Sie nicht andere Codecs verwenden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top