Come leggere le righe di un file in Ruby


238

Stavo cercando di utilizzare il seguente codice per leggere le righe da un file. Ma quando si legge un file , i contenuti sono tutti in una riga:

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

Ma questo file stampa ogni riga separatamente.


Devo usare stdin, tipo ruby my_prog.rb < file.txt, dove non posso supporre quale sia il carattere di fine riga che utilizza il file. Come posso gestirlo?


7
Piuttosto che fare line_num = 0, potresti usare each.each_with_indexo possibilmente each.with_index.
Andrew Grimm,

@ andrew-grimm grazie, rende il codice più pulito.
disegna

Vedere stackoverflow.com/q/25189262/128421 per il motivo per cui si preferisce l' Io linea per linea rispetto all'utilizzo read.
Tin Man,

Utilizzare line.chompper gestire le terminazioni di linea (per gentile concessione di @SreenivasanAC )
Yarin

Risposte:


150

Credo che la mia risposta copra le tue nuove preoccupazioni sulla gestione di qualsiasi tipo di terminazioni di linea poiché entrambe "\r\n"e "\r"vengono convertite in standard Linux "\n"prima di analizzare le linee.

Per supportare il "\r"carattere EOL insieme al normale "\n"e "\r\n"da Windows, ecco cosa farei:

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

Naturalmente questa potrebbe essere una cattiva idea su file molto grandi poiché significa caricare l'intero file in memoria.


Quella regex non ha funzionato per me. Il formato Unix usa \ n, windows \ r \ n, mac usa \ n - .gsub (/ (\ r | \ n) + /, "\ n") ha funzionato per me in tutti i casi.
Pod

4
Dovrebbe essere la regex corretta /\r?\n/che coprirà sia \ r \ n che \ n senza combinare le righe vuote come farebbe il commento di Pod
Irongaze.com

12
Questo leggerà l'intero file in memoria, il che potrebbe essere impossibile a seconda della dimensione del file.
eremzeit,

1
Questo metodo è molto altamente inefficiente, talabes risposta qui stackoverflow.com/a/17415655/228589 è la risposta migliore. Verifica l'implementazione di questi due metodi.
CantGetANick,

1
Questo non è il modo rubino. La risposta di seguito mostra il comportamento giusto.
Merovex,

525

Ruby ha un metodo per questo:

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

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


questo methond più lento di methond che è @Olivier L.
HelloWorld

1
@HelloWorld Probabilmente perché sta eliminando ogni riga precedente dalla memoria e caricando in memoria ogni riga. Potrebbe essere sbagliato, ma Ruby probabilmente sta facendo le cose correttamente (in modo che i file di grandi dimensioni non causino l'arresto anomalo dello script).
Starkers,

Puoi usarlo anche with_indexcon questo?
Joshua Pinter,

1
Sì, è possibile, ad esFile.readlines(filename).each_with_index { |line, i| puts "#{i}: #{line}" }
Wulftone,

Questo metodo sembra migliore. Sto leggendo file molto grandi e in questo modo l'applicazione non si arresta in modo anomalo tentando di caricare l'intero file in memoria contemporaneamente.
Shelby S


18

Il tuo primo file ha terminazioni di riga di Mac Classic (che è "\r"invece del solito "\n"). Aprilo con

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

per specificare le terminazioni di riga.


1
Purtroppo, non c'è niente come le nuove linee universali in Python, almeno che io sappia.
Josh Lee,

ancora una domanda, devo usare stdin, come ruby ​​my_prog.rb <file.txt, dove non posso supporre quale sia il carattere di fine riga usato dal file ... Come posso gestirlo?
disegna

La risposta di Olivier sembra utile, se stai bene caricando l'intero file in memoria. Il rilevamento di nuove righe durante la scansione del file richiederà un po 'più di lavoro.
Josh Lee,

7

È a causa delle linee di fondo in ogni linea. Utilizzare il metodo chomp in ruby ​​per eliminare la fine '\ n' o 'r' alla fine.

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

2
@SreenivisanAC +1 per chomp!
Yarin,

7

Sono parziale del seguente approccio per i file con intestazioni:

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

Ciò consente di elaborare una riga (o righe) di intestazione in modo diverso rispetto alle righe di contenuto.


6

che ne dici di ottenere ?

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

4

Non dimenticare che se sei preoccupato di leggere in un file che potrebbe avere enormi righe che potrebbero inondare la tua RAM durante il runtime, puoi sempre leggere il file pezzo-pasto. Vedi " Perché slurping un file è male ".

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.