Cómo leer líneas de un archivo en Ruby

238

Estaba tratando de usar el siguiente código para leer líneas de un archivo. Pero al leer un archivo , todo el contenido está en una línea:

line_num=0
File.open('xxx.txt').each do |line|
  print "#{line_num += 1} #{line}"
end

Pero este archivo imprime cada línea por separado.


Tengo que usar stdin, como ruby my_prog.rb < file.txt, donde no puedo asumir cuál es el carácter de final de línea que usa el archivo. ¿Cómo puedo manejarlo?

dibujar
fuente
77
En lugar de hacerlo line_num = 0, podría usar each.each_with_indexo posiblemente each.with_index.
Andrew Grimm
@ Andrew-Grimm gracias, hace un código más limpio.
sorteo
Consulte stackoverflow.com/q/25189262/128421 para ver por qué se prefiere IO línea por línea sobre el uso read.
The Tin Man
Úselo line.chomppara manejar los finales de línea (cortesía de @SreenivasanAC )
Yarin

Respuestas:

150

Creo que mi respuesta cubre sus nuevas preocupaciones sobre el manejo de cualquier tipo de finales de línea, ya que ambos "\r\n"y "\r"se convierten en estándar de Linux "\n"antes de analizar las líneas.

Para admitir el "\r"carácter EOL junto con el normal "\n", y "\r\n"desde Windows, esto es lo que haría:

line_num=0
text=File.open('xxx.txt').read
text.gsub!(/\r\n?/, "\n")
text.each_line do |line|
  print "#{line_num += 1} #{line}"
end

Por supuesto, esto podría ser una mala idea en archivos muy grandes, ya que significa cargar todo el archivo en la memoria.

Olivier L.
fuente
Esa expresión regular no funcionó para mí. El formato Unix usa \ n, windows \ r \ n, el Mac usa \ n - .gsub (/ (\ r | \ n) + /, "\ n") funcionó para mí en todos los casos.
Pod
44
La expresión regular correcta debería ser la /\r?\n/que cubrirá tanto \ r \ n como \ n sin combinar líneas vacías como lo haría el comentario de Pod
Irongaze.com
12
Esto leerá todo el archivo en la memoria, lo que podría ser imposible dependiendo de qué tan grande sea el archivo.
eremzeit
1
Este método es muy ineficiente, talabes responde aquí stackoverflow.com/a/17415655/228589 es la mejor respuesta. Verifique la implementación de estos dos métodos.
CantGetANick
1
Este no es el camino del rubí. La respuesta a continuación muestra el comportamiento correcto.
Merovex
525

Ruby tiene un método para esto:

File.readlines('foo').each do |line|

http://ruby-doc.org/core-1.9.3/IO.html#method-c-readlines

Jonathan
fuente
este método es más lento que el método que es @Olivier L.
HelloWorld
1
@HelloWorld Probablemente porque está borrando cada línea anterior de la memoria y cargando cada línea en la memoria. Puede estar equivocado, pero es probable que Ruby esté haciendo las cosas correctamente (para que los archivos grandes no provoquen un bloqueo del script).
Starkers
¿Puedes usar with_indexesto también?
Joshua Pinter
1
Sí, puede, por ejemploFile.readlines(filename).each_with_index { |line, i| puts "#{i}: #{line}" }
wulftone
Este método parece mejor. Estoy leyendo archivos muy grandes y de esta manera no bloquea la aplicación al intentar cargar todo el archivo en la memoria a la vez.
Shelby S
393
File.foreach(filename).with_index do |line, line_num|
   puts "#{line_num}: #{line}"
end

Esto ejecutará el bloque dado para cada línea en el archivo sin arrastrar todo el archivo a la memoria. Ver: IO :: foreach .

talabes
fuente
10
Esta es la respuesta: Ruby idiomático y no sorbe el archivo. Ver también stackoverflow.com/a/5546681/165673
Yarin
44
¡Todos saluden a los dioses rubíes!
Joshua Pinter
¿Cómo ir a la segunda línea dentro del bucle?
user1735921
18

Su primer archivo tiene finales de línea Mac Classic (eso es en "\r"lugar de lo habitual "\n"). Ábrelo con

File.open('foo').each(sep="\r") do |line|

para especificar las terminaciones de línea.

Josh Lee
fuente
1
Lamentablemente, no hay nada como las nuevas líneas universales en Python, al menos que yo sepa.
Josh Lee
una pregunta más, tengo que usar stdin, como ruby ​​my_prog.rb <file.txt, donde no puedo suponer qué usa la línea que termina el archivo ... ¿Cómo puedo manejarlo?
sorteo
La respuesta de Olivier parece útil, si está de acuerdo con cargar todo el archivo en la memoria. Detectar nuevas líneas mientras aún escanea el archivo tomará un poco más de trabajo.
Josh Lee
7

Se debe a las líneas finales en cada línea. Use el método chomp en ruby ​​para eliminar la línea final '\ n' o 'r' al final.

line_num=0
File.open('xxx.txt').each do |line|
  print "#{line_num += 1} #{line.chomp}"
end
Sreenivasan AC
fuente
2
@SreenivisanAC +1 para chomp!
Yarin
7

Soy parcial con el siguiente enfoque para los archivos que tienen encabezados:

File.open(file, "r") do |fh|
    header = fh.readline
    # Process the header
    while(line = fh.gets) != nil
        #do stuff
    end
end

Esto le permite procesar una línea de encabezado (o líneas) diferente a las líneas de contenido.

Ron Gejman
fuente
6

¿qué tal se pone ?

myFile=File.open("paths_to_file","r")
while(line=myFile.gets)
 //do stuff with line
end
JBoy
fuente
4

No olvide que si le preocupa leer un archivo que podría tener grandes líneas que podrían saturar su RAM durante el tiempo de ejecución, siempre puede leer el archivo por partes. Consulte " Por qué es malo sorber un archivo ".

File.open('file_path', 'rb') do |io|
  while chunk = io.read(16 * 1024) do
    something_with_the chunk
    # like stream it across a network
    # or write it to another file:
    # other_io.write chunk
  end
end
Nels
fuente