2017-02-03 1 views
2

ich für eine Lösung im Grunde bin auf der Suche, die mich um die Linien zu streamen und sie in der gleichen Datei zu ersetzen, a la Files.linesJeder Mechanismus in Java 8/NIO zum Ersetzen der Zeilen einer großen Datei, ohne sie in den Speicher zu laden?

+0

Um zu tun, was Sie vorschlagen, Ihre Datei müsste in einer Weise formatiert werden, die diese Art von Änderung ermöglicht. eine Datenstruktur.Eine flache Textdatei muss von dem Punkt, an dem Sie sie ändern, neu geschrieben werden (es sei denn, Sie ändern die Länge nicht) –

+0

Wäre es eine Option, Shell-Befehle in einem Prozess zu verwenden? Wie bei Linux könntest du eine Art 'sed'-Befehl ausgeben ... Ich weiß, das würde Nachteile wie OS-Dependency mit sich bringen, aber wenn es deine einzige Chance ist ... – Fildor

+0

@Fildor 'sed' funktioniert nicht * in situ *. – EJP

Antwort

2

Jeder Mechanismus in Java 8/NIO die Linien einer großen Datei für den Ersatz ohne es in den Speicher zu laden?

Grundsätzlich keine.

Jede Änderung an einer Datei, bei der die Anzahl der Bytes zwischen den Offsets A und B geändert werden muss, kann nur durch Neuschreiben der Datei oder durch Erstellen einer neuen Datei erfolgen. In jedem Fall muss alles nach B in den Speicher geladen/gelesen werden.

Dies ist keine Java-spezifische Einschränkung. Dies ist eine Folge der Art und Weise, in der moderne Betriebssysteme Dateien darstellen, und die Low-Level-APIs (z. B. Syscall), die sie für Anwendungen bereitstellen.


Im besonderen Fall, wo man eine Zeile (oder eine Folge von Linien) mit einer Linie (oder eine Folge von Linien) von exakt die gleichen Länge ersetzt wird, dann kann man den Austausch tut entweder Random, oder durch Abbilden die Datei in den Speicher. Beachten Sie, dass der letztere Ansatz nicht dazu führt, dass die gesamte Datei in den Speicher gelesen wird.

Es ist auch möglich, Zeilen zu ersetzen oder zu löschen, während die Datei "vor Ort" aktualisiert wird (Änderung der Dateilänge ...). Ein Beispiel finden Sie in @Sergio Montoros Antwort. Bei einer Aktualisierung vor Ort besteht jedoch das Risiko, dass die Datei beschädigt wird, wenn die Anwendung unterbrochen wird. Und das beinhaltet das Lesen und Neuschreiben aller Bytes in der Datei nach dem Einfüge-/Löschpunkt. Und das bedeutet, sie in den Speicher zu laden.

1

Es gab einen Mechanismus in Java 1: RandomAccessFile; aber jeder solche In-Place-Mechanismus erfordert, dass Sie den Start-Offset der Linie kennen, und dass die neue Linie die gleiche Länge wie die alte hat.

Andernfalls müssen Sie die Datei bis zu dieser Zeile kopieren, die neue Zeile in der Ausgabe ersetzen und dann die Kopie fortsetzen.

Sicherlich müssen Sie nicht die gesamte Datei in den Speicher laden.

+0

Dieser Mechanismus existiert noch. Allerdings bietet NIO eine Speicherzuordnung, die für diesen Anwendungsfall möglicherweise eine höhere Leistung bietet. – Holger

1

Ja.

Ein FileChannel ermöglicht zufälliges Lesen/Schreiben an jede Position einer Datei. Wenn Sie einen Puffer für den Lesezugriff haben, der lang genug ist, können Sie Zeilen ersetzen, selbst wenn die neue Zeile länger als die vorherige ist.

Das folgende Beispiel ist eine Spielzeugimplementierung, die zwei Annahmen macht: 1.) die Eingabedatei ist ISO-8859-1 Unix LF codiert und 2.) jede neue Zeile wird nie länger sein als die nächste Zeile (eine Zeile gelesen) Vorauspuffer).

Wenn Sie keine temporäre Datei erstellen können, sollten Sie diesen Ansatz mit dem natürlicheren Stream in -> stream out vergleichen, da ich nicht weiß, welche Leistung ein drehendes Laufwerk Ihnen für einen Algorithmus bietet, der sich ständig weiterentwickelt rückwärts in einer Datei.

import java.nio.file.Path; 
import java.nio.file.Paths; 
import java.nio.ByteBuffer; 
import java.nio.channels.FileChannel; 
import static java.nio.file.StandardOpenOption.*; 

import java.io.IOException; 

public class ReplaceInFile { 

    public static void main(String args[]) throws IOException { 
     Path file = Paths.get(args[0]); 
     ByteBuffer writeBuffer; 
     long readPos = 0l; 
     long writePos; 
     String line_m; 
     String line_n; 
     String line_t; 
     FileChannel channel = FileChannel.open(file, READ, WRITE); 
     channel.position(0); 
     writePos = readPos; 
     line_m = readLine(channel); 
     do { 
      readPos += line_m.length() + 1; 
      channel.position(readPos); 
      line_n = readLine(channel); 
      line_t = transformLine(line_m)+"\n"; 
      writeBuffer = ByteBuffer.allocate(line_t.length()+1); 
      writeBuffer.put(line_t.getBytes("ISO8859_1")); 
      System.out.print("replaced line "+line_m+" with "+line_t);   
      channel.position(writePos); 
      writeBuffer.rewind(); 
      while (writeBuffer.hasRemaining()) { 
       channel.write(writeBuffer); 
      } 
      writePos += line_t.length(); 
      line_m = line_n; 
      assert writePos > readPos; 
     } while (line_m.length() > 0); 
     channel.close(); 
     System.out.println("Done!"); 
    } 

    public static String transformLine(String input) throws IOException { 
     return input.replace("<", "&lt;").replace(">", "&gt;"); 
    } 

    public static String readLine(FileChannel channel) throws IOException { 
     ByteBuffer readBuffer = ByteBuffer.allocate(1); 
     StringBuffer line = new StringBuffer(); 
     do { 
      int read = channel.read(readBuffer); 
      if (read<1) break; 
      readBuffer.rewind(); 
      char c = (char) readBuffer.get(); 
      readBuffer.rewind(); 
      if (c=='\n') break; 
      line.append(c); 
     } while (true); 
     return line.toString(); 
    } 

} 
Verwandte Themen