2013-05-19 13 views
11

So come scrivere su un file e leggere da un file, ma non so come modificare un file oltre a leggere l'intero file in memoria, manipolandolo e riscrivendo l'intero file. Per file di grandi dimensioni questo non è molto produttivo.Come rimuovere righe di dati nel mezzo di un file di testo con Ruby

Non so davvero la differenza tra append e write.

E.g.

Se ho un file che contiene:

Person1,will,23 
Person2,Richard,32 
Person3,Mike,44 

Come potrei essere in grado solo di cancellare riga contenente Person2?

+0

Sawa, mi stai sempre aiutando. Quindi ogni volta che un programma salva un file, sovrascrive l'intero file? – Senjai

+0

Come pensate di trovare quali linee rimuovere senza leggere il file? E 'sempre un certo numero di linea? –

+0

@Senjai Sergio suggerisce qualcosa che potrebbe aiutare, e se questo è corretto, allora i miei commenti precedenti sono sbagliati. Mi dispiace per quello – sawa

risposta

13

È possibile eliminare una riga in diversi modi:

  • Simula eliminazione. Cioè, basta sovrascrivere il contenuto della linea con spazi. In seguito, quando leggi ed elabori il file, ignora semplicemente tali righe vuote.

    Pro: questo è facile e veloce. Contro: non è la vera eliminazione dei dati (il file non si riduce) ed è necessario fare più lavoro durante la lettura/elaborazione del file.

    Codice:

    f = File.new(filename, 'r+') 
    f.each do |line| 
        if should_be_deleted(line) 
        # seek back to the beginning of the line. 
        f.seek(-line.length, IO::SEEK_CUR) 
    
        # overwrite line with spaces and add a newline char 
        f.write(' ' * (line.length - 1)) 
        f.write("\n") 
        end 
    end 
    f.close 
    
    File.new(filename).each {|line| p line } 
    
    # >> "Person1,will,23\n" 
    # >> "     \n" 
    # >> "Person3,Mike,44\n" 
    
  • do Real eliminazione. Ciò significa che la linea non esisterà più. Quindi dovrai leggere la riga successiva e sovrascrivere la riga corrente con essa. Quindi ripetere questo per tutte le seguenti righe fino al raggiungimento della fine del file. Questo sembra un task soggetto a errore (linee di diversa lunghezza, ecc.), Ecco una alternativa senza errori: apri il file temporaneo, scrivici fino a (ma non includendo) la riga che vuoi eliminare, salta la linea vuoi cancellare, scrivi il resto nel file temp. Elimina il file originale e rinominalo temporaneamente per usarne il nome. Fatto.

    Anche se tecnicamente è una riscrittura totale del file, è diverso da quello che hai chiesto. Il file non ha bisogno di essere caricato completamente in memoria. Hai bisogno di una sola linea per volta. Ruby fornisce un metodo per questo: IO#each_line.

    Pro: Nessun presupposto. Le righe vengono cancellate. Il codice di lettura non deve essere modificato. Contro: molto altro lavoro quando si cancella la linea (non solo il codice, ma anche il tempo IO/CPU).

    C'è uno snippet che illustra questo approccio in @ azgult's answer.

+0

È possibile sovrascrivere solo una parte di un file (con spazi) senza sovrascrivere l'intero file? – sawa

+1

Certo, è possibile. Apri un file in modalità scrittura, cerca l'offset necessario e inizia a scrivere. –

+0

La modalità non di scrittura, lettura/scrittura (il flag 'r +') è necessaria per sovrascrivere le parti. – azgult

0

here Leggi:

File.open('output.txt', 'w') do |out_file| 
    File.open('input.txt', 'r').each do |line| 
    out_file.print line.sub('Person2', '') 
    end 
end 
+0

Sovrascrive l'intero file. Penso che l'OP sappia come farlo, e non è quello che viene chiesto. – sawa

+1

Sembra che questo sia l'unico modo in base ad altre risposte. – juanpastas

3

si potrebbe aprire il file e leggerlo riga per riga, aggiungendo linee che si desidera conservare in un nuovo file. Ciò consente il massimo controllo su quali linee vengono mantenute, senza distruggere il file originale.

File.open('output_file_path', 'w') do |output| # 'w' for a new file, 'a' append to existing 
    File.open('input_file_path', 'r') do |input| 
    line = input.readline 
    if keep_line(line) # logic here to determine if the line should be kept 
     output.write(line) 
    end 
    end 
end 

Se si conosce la posizione di inizio e la fine del pezzo che si desidera rimuovere, è possibile aprire il file, leggere al punto di partenza, poi cercare fino alla fine e continuare a leggere.

Guardi su parametri al metodo read, e leggere che cercano qui:

http://ruby-doc.org/core-2.0/IO.html#method-i-read

4

Quando i file vengono salvati in sostanza come un blocco continuo di dati sul disco, rimuovere qualsiasi parte del fine necessaria riscrittura a almeno quello che viene dopo. Questo in sostanza significa che, come dici tu, non è particolarmente efficiente per file di grandi dimensioni. È quindi generalmente una buona idea limitare le dimensioni dei file in modo che tali problemi non si verifichino.

Alcune soluzioni "di compromesso" potrebbero essere copiare il file riga per riga in un secondo file e quindi spostarlo per sostituire il primo. Questo evita il caricamento del file in memoria, ma non evita qualsiasi accesso del disco rigido:

require 'fileutils' 

open('file.txt', 'r') do |f| 
    open('file.txt.tmp', 'w') do |f2| 
    f.each_line do |line| 
     f2.write(line) unless line.start_with? "Person2" 
    end 
    end 
end 
FileUtils.mv 'file.txt.tmp', 'file.txt' 

Ancora più efficiente sarebbe quello di lettura-scrittura aprire il file e passare direttamente alla posizione che si desidera eliminare e poi passare il resto dei dati indietro - ma ciò renderebbe un codice abbastanza brutto (e non posso chiederlo di farlo ora).

Problemi correlati