2011-12-24 16 views
12

Me gustaría añadir una sola línea a la parte superior de un archivo con Ruby como esto:Anteponer una sola línea para presentar con Ruby

# initial file contents 
something 
else 

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

El siguiente código simplemente se cambia el contenido de todo el archivo:

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

Respuesta

19

Esta es una tarea bastante común:

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

Configure la prueba:

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

Este es el código real:

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

Cambie el nombre del archivo antiguo a algo seguro:

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

Demostrar que funciona:

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

que da salida:

hello 
something 
else 
--- 
something 
else 

No desea intentar cargar el archivo completamente en la memoria. Eso funcionará hasta que obtenga un archivo que sea más grande que su asignación de RAM, y la máquina se ralentiza o, peor aún, se cuelga.

En su lugar, léelo línea por línea. Leer líneas individuales es aún extremadamente rápido y escalable. Deberá tener suficiente espacio en su disco para almacenar el archivo original y el temporal.

2

No existe ningún mecanismo para hacer lo que quiere hacer fácilmente.

En su lugar, deberá abrir el archivo, eliminar el archivo, abrir un archivo nuevo con el nombre antiguo para escribir, escribir su contenido y luego escribir el nuevo archivo a partir del contenido del archivo anterior. Eso es bastante enrevesada sonando, pero es sencillo de código:

$ 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 

Tenga en cuenta que el mecanismo que he usado no se molesta en comprobar los errores - es probable que debe manejar los errores en cada File.open() solicitud y la solicitud File.unlink() - y asume que todo el contenido del archivo cabe en la memoria. Un breve ejemplo:

$ 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 
$ 

Si desea gestionar los archivos que podrían no encajar completamente en la memoria, se debe codificar un bucle algo así como esto (no probado - mejor lo consideran pseudo-código):

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

Como alternativa, puede escribir en un archivo temporal, y si el proceso de escritura tiene éxito, y luego elimine el archivo y cambie el nombre del archivo temporal en su lugar. Cualquiera que sea el enfoque que tenga más sentido para ti.

7

fwiw esto parece funcionar:

#!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

Sea cuidadoso sorbiendo el archivo. No es una solución escalable leer un archivo completo en la memoria. –

+0

Me gusta esto para retoques simples, personalmente. – steve

+0

La mejor solución para el caso simple. –

2

Usted puede intentar esto:

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

He llegó con algo como esto, es un poco más descriptivo y menos críptica que otras soluciones que he 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 

Y se puede utilizar de esta manera:

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

¡Gran respuesta! Puede reutilizarlo a través de una aplicación. La mejor solución IMO. ¡Gracias! – sjsc

2

Como han dicho algunos, probablemente no use esto para archivos más grandes, pero es un comienzo simple.

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

Desde la línea de comandos, puede hacerlo:

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

o de manera más eficiente

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

Por desgracia, resulta que las opciones -i (en el lugar) no es realmente in situ, sin embargo (y tampoco es sed s opción en el lugar para el caso) - mi archivo tendrá un inodo diferente después de la operación.

Eso me entristeció, porque efectivamente, no puedo filtrar (preceder) a un archivo enorme si no tengo suficiente espacio de disco para dos copias.

No es realmente difícil hacerlo en el lugar, sin embargo (filtrando en general, no solo para la primera línea). Todo lo que necesita hacer es:

1) asegúrese, tiene punteros de lectura y escritura separados (o objetos de archivo separados para leer y escribir) 2) asegúrese de haber almacenado las partes no leídas que va a reescribir 3) truncar al archivo al final si su operación de filtrado debe terminar con un archivo más corto

Escribí la clase de contenedor para eso en https://github.org/pjump/i_rewriter.

Con él, usted puede hacer

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

o con el ejecutable:

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

Uso cuidadosamente (que puede corromper el archivo si se interrumpe).

0

Puro, pero funciona. Minimice las operaciones relacionadas con archivos.

`#!/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 
    }` 
Cuestiones relacionadas