Schreibe eine zirkuläre Datei in C ++

7

Ich muss eine zirkuläre Datei in C ++ schreiben. Das Programm muss Zeilen in eine Datei schreiben und wenn der Code eine maximale Anzahl von Zeilen erreicht, muss es die Zeilen am Anfang der Datei überschreiben.

Hat jemand eine Idee?

    
Kram 20.05.2009, 12:44
quelle

13 Antworten

9

Leider können Sie Zeilen am Anfang einer Datei nicht abschneiden / überschreiben, ohne das gesamte Ding neu zu schreiben.

Neuer Vorschlag

Ich habe gerade an einen neuen Ansatz gedacht, der Ihnen vielleicht helfen könnte ...

Sie könnten Ihrer Datei einen kleinen Header hinzufügen, der die folgende Struktur hat:

Bearbeiten: Quatsch, ich habe gerade eine Variante eines Umlaufspeichers beschrieben!

Kopfzeilenfelder

  • Bytes 00 - 07 (long) - Gesamtzahl (aktuelle) Anzahl der in die Datei geschriebenen Zeilen.
  • Bytes 08 - 15 (long) - Zeiger auf den Anfang der "tatsächlichen" ersten Zeile Ihrer Datei. Dies ist zunächst das Byte nach dem Ende des Headers, wird aber später geändert, wenn Daten überschrieben werden. '
  • Bytes 16 - 23 (long) - Länge des "Endabschnitts" der Datei. Auch dies wird zunächst Null sein, wird sich aber später ändern, wenn Daten überschrieben werden.

Lesealgorithmus (Pseudocode)

Liest die gesamte Datei.

%Vor%

Schreibalgorithmus (Pseudocode)

Schreibt eine beliebige Anzahl neuer Zeilen in die Datei.

%Vor%

Nicht ein schrecklich einfacher Algorithmus, ich gebe es zu! Trotzdem denke ich, dass es in gewisser Weise ziemlich elegant ist. Lassen Sie mich wissen, wenn das nicht klar ist. Hoffentlich sollte es genau das tun, was Sie jetzt wollen.

Originalvorschlag

Nun, wenn Ihre Zeilen garantiert eine konstante Länge (in Bytes) haben, könnten Sie einfach genug zurück zum entsprechenden Punkt suchen und existierende Daten überschreiben. Dies scheint jedoch eine eher unwahrscheinliche Situation zu sein. Wenn es Ihnen nichts ausmacht, die Beschränkung aufzuerlegen, dass Ihre Zeilen eine maximale Länge haben müssen, und zusätzlich jede der Zeilen, die Sie schreiben, auf diese maximale Länge aufzufüllen, dann könnte das die Sache für Sie leicht machen. Dennoch hat es seine Nachteile, wie zum Beispiel eine stark erhöhte Dateigröße unter bestimmten Umständen (d. H. Die meisten Zeilen sind viel kürzer als die maximale Länge). Es hängt alles von der Situation ab, ob dies akzeptabel ist oder nicht ...

Schließlich möchten Sie vielleicht stattdessen ein vorhandenes Protokollierungssystem verwenden, abhängig von Ihrem genauen Zweck.

    
Noldorin 20.05.2009, 12:48
quelle
7

Die übliche Methode zur Protokollierung, die nicht in der Größe explodiert, ist die Verwendung von Rolling-Log-Dateien, die einmal täglich oder ähnlich wiederholt werden und nur die letzten N Dateien enthalten.

Zum Beispiel erstellen Sie jeden Tag eine neue Logdatei mit dem Dateinamen 'application_2009_05_20.log' und beginnen mit dem Schreiben, immer anhängend.

Sobald Sie 14 Tage lang Logdateien haben, löschen Sie die ältesten.

    
quelle
5

Da Dateien Byte-orientiert sind und Sie einen zeilenorientierten Dienst benötigen, haben Sie zwei Möglichkeiten:

  1. implementiert einen zeilenorientierten Wrapper um die Datei

  2. wechseln Sie zu einem zeilenorientierten Gerät. Nur von ganzem Herzen: SQLite hat einige nette C ++ - Wrapper zur Verfügung.

xtofl 20.05.2009 13:02
quelle
2

Verwenden Sie einen Ringpuffer und schreiben Sie den Puffer in eine Datei für jedes Add.

Hier ist eine kleine und einfache Codegröße Lösung. Es ist ein einfacher Ringspeicher für Strings und jedes Mal, wenn Sie Strings hinzufügen, schreibt er den gesamten String-Puffer in die Datei (natürlich entstehen Ihnen signifikante Kosten für das Schreiben all Strings für einen einzelnen Add-Vorgang, das ist nur für eine kleine Anzahl von Strings geeignet).

Einfache Implementierung des Ringspeichers mit Ausgabe in eine Datei:

%Vor%

Also hatten wir im obigen Beispiel 5 Zeilen Eingabe und unser Puffer war nur 4 Zeilen lang. Daher sollte die Ausgabe nur 4 Zeilen haben und die erste Zeile sollte durch die letzte Zeile "Goodbye world" überschrieben werden. Sicher genug, die erste Zeile der Ausgabe bestätigt "Goodbye World":

%Vor%     
Trevor Boyd Smith 20.05.2009 14:43
quelle
1

Einfache Lösung:

  1. Habe eine Art Delimeter für Linien.
  2. Jedes Mal, wenn Sie eine neue Zeile hinzufügen, überschreiben Sie einfach den gesamten Text von der aktuellen Zeile bis zu einem Trennzeichen.
  3. Das Ende der Datei ist ein Sonderfall und kann mit einer Auffüllung versehen sein, um die Dateigröße konstant zu halten.

Diese Lösung bietet eine konstante Dateilänge anstelle einer konstanten Anzahl von Zeilen innerhalb der Datei. Die Anzahl der Linien variiert mit der Zeit abhängig von der Länge. Diese Lösung erschwert das schnelle Suchen nach bestimmten Zeilennummern. Sie können jedoch einige Indikatordaten oben oder unten an der Datei anheften, um dies zu vereinfachen.

"Clever" Lösung (Variation der obigen Lösung):

Verwenden Sie einfach den gleichen Trick, der manchmal für Deques verwendet wird. Gehen Sie einfach vom Anfang der Datei bis zum Ende, aber verfolgen Sie, wo der Anfang / das Ende der Datei ist. Sie könnten ein Unwrap-Dienstprogramm schreiben, um diese Datei in eine Standarddatei umzuwandeln, wenn Sie sie mit einem Programm lesen wollten, das sie nicht unterstützt. Diese Lösung ist wirklich einfach zu implementieren, aber ich mag die obige Version besser.

Hässliche Lösung:

Fügen Sie beim Hinzufügen von Zeilen eine moderate Menge an Auffüllung zu jeder hinzugefügten Zeile hinzu.

Jedes Mal, wenn Sie eine neue Zeile hinzufügen möchten, gehen Sie wie folgt vor:

  1. Ermitteln Sie die Länge der aktuellen Zeile einschließlich Auffüllen. Beachten Sie, dass der Anfang der aktuellen Zeile gleich dem Ende der vorherigen Zeile ohne Auffüllung ist.
  2. Wenn die aktuelle Zeile lang genug ist, um in die Zeile zu passen, in der Sie sich befinden, fügen Sie sie hinzu. Fügen Sie am Ende der vorherigen Zeile den linken Abstand hinzu, der 1/3 des überschüssigen Platzes entspricht. 3 von überschüssigem Raum.
  3. Wenn die aktuelle Linie nicht lang genug ist, um in die Linie zu passen, in der Sie sich befinden, verschieben Sie die Linien vor Ihnen (essen ihre Polsterung), bis ihr Platz ist.
  4. Wenn Schritt 3 eine Art Schwellenwert erreicht, überschreiben Sie die gesamte Datei mit mehr Padding.

Beachten Sie, dass dies ziemlich schlecht funktioniert, es sei denn, Ihre Zeilen sind hinsichtlich der Länge ziemlich konsistent. Eine einfachere Lösung besteht darin, zu garantieren, dass Zeilen von konstanter Länge sind (aber in irgendeiner Weise dazu verwendet werden, mehrzeilige "Zeilen" zu erzeugen, falls Sie diese Länge überschreiten.

    
Brian 20.05.2009 13:03
quelle
1

Wenn die Dateien Textdateien sein müssen:
Dies ist bei unterschiedlichen Leitungslängen sehr problematisch. Ihre ersten zwei Zeilen sind jeweils 80 Zeichen, wie überschreiben Sie das mit einer Zeile mit 100 Zeichen?

Wenn die neue Zeile die erste Zeile ersetzen soll, würde dies zu einer Dateieinfügung führen, was eine sehr teure Operation ist (im Grunde muss der gesamte Rest der Datei gelesen und geschrieben werden) . Das wollen Sie wirklich nicht für alle, sondern nur für minimale Datenmengen tun.

Wenn dies für den Protokollierungszweck ist, verwenden Sie rollng-Protokolldateien - z. einen Tag (wie von Lassevek vorgeschlagen). Ich habe es noch einfacher gemacht: Wenn die Dateigröße ein Limit überschreitet, wird die alte Datei in .bak umbenannt (alte .bak wird gelöscht) und neu gestartet. Mit einer Grenze von 1 MB erhält dies z.B. die letzten 1 MB, während nie mehr als 2 MB belegen.

Sie könnten einen ähnlichen Mechanismus mit zwei oder mehr Dateien verwenden. Verschieben Sie den "Rollover" grundsätzlich in Dateien und nicht in Zeilen.

Wenn die Datei ein proprietäres Format hat:
Verwenden Sie eine einfache DB-Engine (wie SQLite wie vorgeschlagen) oder einen anderen strukturierten Speichermechanismus.

    
peterchen 20.05.2009 14:02
quelle
1

Sie könnten log4cxx mit einem RollingFileAppender , um diese Informationen in eine Protokolldatei zu schreiben. Der RollingFileAppender behandelt das Überrollen der Protokolldatei, wenn sie eine bestimmte Größe erreicht. Ich denke nicht, dass es genau ist, was du willst, aber es ist ziemlich einfach - vielleicht wird es das tun.

    
Paul Morie 20.05.2009 14:05
quelle
1

Erstellen Sie einfach ein Mapping der Datei der benötigten Größe (CreateFileMapping oder mmap), schreiben Sie die Zeilen in den Puffer und beginnen Sie von vorne, wenn die maximale Anzahl erreicht ist.

    
Edouard A. 20.05.2009 14:08
quelle
0

Das wird schwierig, da Datei-I / O mit Bytes als der zugrunde liegenden Speichereinheit und nicht mit Zeilen arbeitet.

Ich meine, Sie könnten einfach zum Anfang zurückgehen und die früheren Daten überlisten, aber ich habe eine Ahnung, dass Sie nicht wollen.

    
Jason S 20.05.2009 12:48
quelle
0

Ich habe das gesehen, indem ich die aktuelle Schreibposition für die Datei irgendwo behalte. Wenn Sie eine Zeile hinzufügen müssen, suchen Sie nach der Position, schreiben die Zeile und aktualisieren die Position atomar. Wenn Sie überlaufen, suchen Sie vor dem Schreiben der Zeile eine Null. Wir tun dies heute für größenbeschränkte zirkuläre Protokolldateien. Es ist etwas seltsam, es auf einer Linie-beschränkten Basis zu tun, aber könnte wahrscheinlich in einer ähnlichen Weise getan werden. Unsere Schreibschleife sieht ungefähr so ​​aus:

%Vor%

Der schwierige Teil besteht darin, die aktuelle Schreibposition beizubehalten und eine Möglichkeit zu finden, das Lesen der Datei zu koordinieren (z. B. mit dem Dienstprogramm tail ), während Ihre Anwendung darauf schreibt. Ihr Leser-Dienstprogramm muss auch die Schreibposition verfolgen, so dass die Leseschleife zu:

wird %Vor%

Dies ist nicht in einer bestimmten Sprache - es ist nur Pseudocode, aber die Idee ist da. Natürlich habe ich die Bearbeitung aller interessanten Randfälle dem Leser überlassen.

Mit all dem gesagt ... stimme ich den anderen Meinungen zu. Tun Sie das nicht, es sei denn, Sie haben einen wirklich guten Grund. Es klingt wie eine großartige Idee, aber:

  • die Implementierung ist schwer zu schreiben
  • es ist noch schwieriger effizient zu machen
  • Da die Schreibposition irgendwo gespeichert werden muss, müssen sich mehrere Dienstprogramme darauf einigen, wie sie gelesen, aktualisiert, initialisiert werden.
  • mit einem nichtlinearen Protokoll erschwert die Protokollverarbeitung mit vorhandenen Tools wie grep , tail , perl usw.

Insgesamt ist es besser, ein vorhandenes Paketprotokollierungspaket zu verwenden, das eine konfigurierbare Protokolldateiverwaltung ermöglicht. Schauen Sie sich Apache log4cxx oder Poco's Poco::Logger .

    
D.Shawley 20.05.2009 13:12
quelle
0

Einfache Problemumgehung:

  • Definieren Sie eine maximale Länge von 80 Zeichen pro Zeile. Wickeln Sie längere "Zeilen" in mehrere Zeilen.
  • Fügen Sie einer Zeile eine Zeilenüberschrift hinzu. wie "[# 589] Dies ist die 589. Zeile", damit Sie wissen, was zuerst kommt usw.
yairchu 20.05.2009 14:03
quelle
0

Wenn Sie diese Datei für die Eingabe in eine andere Anwendung generieren möchten, sollten Sie sich am besten direkt in einer relationalen Datenbank anmelden (SQL Server, MySQL, was auch immer ..) die protokollierten Daten.

    
Eclipse 20.05.2009 14:54
quelle
0

Um das Ding mit variabler Größe zu umgehen, werden Sie wahrscheinlich ein Indirektionsschema und ein Zuweisungsschema erhalten. Dies würde aus einem indirekten Block mit einer festen Anzahl von "Zeigern" in der Datei und einem "nächsten zu schreibenden" Zeiger bestehen, der um N herumgehen würde.

Aber der Haupttrick wäre das Hinzufügen der Indirektion.

    
xtofl 20.05.2009 17:46
quelle

Tags und Links