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?


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.


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


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


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.


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

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.


6

¿qué tal se pone ?

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

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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.