2011-12-24 6 views
12

vorrei aggiungere una sola riga alla parte superiore di una delle file con Rubino in questo modo:Prepend una sola linea di file con Ruby

# initial file contents 
something 
else 

# file contents after prepending "hello" on its own line 
hello 
something 
else 

Il seguente codice sostituisce solo il contenuto del tutto il file:

f = File.new('myfile', 'w') 
f.write "test string" 

risposta

19

Questo è un compito piuttosto comune:

original_file = './original_file' 
new_file = original_file + '.new' 

Impostare il test:

File.open(original_file, 'w') do |fo| 
    %w[something else].each { |w| fo.puts w } 
end 

Questo è il codice vero e proprio:

File.open(new_file, 'w') do |fo| 
    fo.puts 'hello' 
    File.foreach(original_file) do |li| 
    fo.puts li 
    end 
end 

Rinominare il vecchio file in qualcosa di sicuro:

File.rename(original_file, original_file + '.old') 
File.rename(new_file, original_file) 

Dimostrare che funziona:

puts `cat #{original_file}` 
puts '---' 
puts `cat #{original_file}.old` 

quali uscite:

hello 
something 
else 
--- 
something 
else 

Non si desidera provare a caricare il file completamente nella memoria. Funzionerà finché non avrai un file più grande dell'allocazione RAM, e la macchina subirà una scansione o, peggio, arresti anomali.

Invece, leggerlo riga per riga. La lettura di singole righe è ancora estremamente veloce ed è scalabile. Dovrai avere abbastanza spazio sul tuo disco per memorizzare l'originale e il file temporaneo.

2

Nessun meccanismo esiste per fare ciò che si vuole fare facilmente.

Invece, è necessario aprire il file, eliminare il file, aprire un nuovo file con il vecchio nome per scrivere, scrivere il contenuto e quindi scrivere il nuovo file dal contenuto del file precedente. Questo è abbastanza contorta dal suono, ma è molto semplice in codice:

$ cat prepend.rb 
#!/usr/bin/ruby 

File.open("passwd", "r") do |orig| 
    File.unlink("passwd") 
    File.open("passwd", "w") do |new| 
     new.write "test string" 
     new.write(orig.read()) 
    end 
end 

Nota che il meccanismo che ho usato non si preoccupa il controllo errori - probabilmente si dovrebbe gestire gli errori su ogni File.open() richiesta e la richiesta File.unlink() - e presume che l'intero contenuto del file si adatterà alla memoria. Un breve esempio:

$ rm passwd 
$ cp /etc/passwd . 
$ ./prepend.rb 
$ head passwd 
test stringroot:x:0:0:root:/root:/bin/bash 
daemon:x:1:1:daemon:/usr/sbin:/bin/sh 
bin:x:2:2:bin:/bin:/bin/sh 
sys:x:3:3:sys:/dev:/bin/sh 
sync:x:4:65534:sync:/bin:/bin/sync 
games:x:5:60:games:/usr/games:/bin/sh 
man:x:6:12:man:/var/cache/man:/bin/sh 
lp:x:7:7:lp:/var/spool/lpd:/bin/sh 
mail:x:8:8:mail:/var/mail:/bin/sh 
news:x:9:9:news:/var/spool/news:/bin/sh 
$ 

Se si desidera gestire i file che potrebbero non adattarsi del tutto in memoria, si dovrebbe codificare un ciclo un po 'come questo (non testato - meglio ritiene pseudo-codice):

while (data=orig.read(4096)) { 
    new.write(data) 
} 

In alternativa, è possibile scrivere su un file temporaneo e, se il processo di scrittura ha esito positivo, quindi eliminare il file e rinominare il file temporaneo in posizione. Qualunque approccio abbia più senso per te.

7

FWIW questo sembra funzionare:

#!usr/bin/ruby 

f = File.open("myfile", "r+") 
lines = f.readlines 
f.close 

lines = ["something\n"] + lines 

output = File.new("myfile", "w") 
lines.each { |line| output.write line } 
output.close 
+5

Fare attenzione a strofinare il file. Non è una soluzione scalabile per leggere un intero file in memoria. –

+0

Mi piace questo per semplice armeggiare, personalmente. – steve

+0

La migliore soluzione per il caso semplice. –

2

si può provare questo:

File.copy_stream(myfile,tempfile) 
f = File.open(myfile,'w') 
f.write("Hello\n#{File.open(tempfile,'r').read}") 
f.close 
File.delete(tempfile) 
2

ho si avvicinò con qualcosa di simile, è un po 'più descrittiva e meno criptico rispetto ad altre soluzioni che ho visto:

def file_prepend(file, str) 
    new_contents = "" 
    File.open(file, 'r') do |fd| 
    contents = fd.read 
    new_contents = str << contents 
    end 
    # Overwrite file but now with prepended string on it 
    File.open(file, 'w') do |fd| 
    fd.write(new_contents) 
    end 
end 

e si può utilizzare in questo modo:

file_prepend("target_file.txt", "hello world!\n") 
+0

Ottima risposta! Può riutilizzarlo attraverso un'app. La migliore soluzione IMO. Grazie! – sjsc

2

Come alcuni hanno già detto, probabilmente non lo si usa per file più grandi, ma è un inizio semplice.

rd = IO.read 'myfile' 
IO.write 'myfile', "hello\n" + rd 
0

Dalla riga di comando, si può fare:

ruby -i -pe '$_= "prepended text\n"+$_ if $. == 1' myfile 

o in modo più efficiente

ruby -i -pe 'BEGIN { gets; print "prepended text\n" + $_ }; ' myfile 

Purtroppo, si scopre, le opzioni -i (sul posto) non è veramente sul posto, però (e nemmeno l'opzione sed s per quella materia) - il mio file avrà un inode diverso dopo l'op.

Questo mi ha reso triste, perché in modo efficace, non posso filtrare (anteporre a) un file enorme se non ho abbastanza spazio su disco per due copie di esso.

Non è davvero difficile farlo sul posto, però (filtraggio in generale, non solo per la prima riga). Tutto quello che dovete fare è:

1) fare in modo, di aver letto separata e scrivere puntatori (o oggetti file separato per la lettura e la scrittura) 2) assicurarsi di aver tamponato le parti non letti che si sta per riscrivere 3) troncare al file alla fine se l'operazione di filtro dovesse finire con un file più breve

Ho scritto la classe wrapper per quello https://github.org/pjump/i_rewriter.

Con esso, si può fare

Rewriter.new("myfile") do |line, index| 
    index == 0 ? "prepended text\n" + line : line 
end 

o con il file eseguibile:

$ i_rewriter 'index == 0 ? "prepended text\n" + line : line' myfile 

Usare con cautela (si può corrompere il file se interrotto).

0

Puro, ma funziona. Riduci al minimo le operazioni relative ai file.

`#!/bin/ruby 
inv_file = File.open("./1.txt","r+") { |f| 
    #Read file string by-string 
    until f.eof? 
     #Searching for some 
     if f.readline[/condition here/] 
      #remember position 
      offset = f.pos 
      #Find it? - stop reading 
      break 
     end 
    end 
    #Remember what contains rest of file 
    tail = f.readlines 
    #Again to offset 
    f.pos = offset 
    #insert what necessary 
    f.puts "INSERTED" 
    #reconstruct tail 
    f.write tail.join 
    }` 
Problemi correlati