Lesen von Zeilen einer Datei in Ruby

237

Ich habe versucht, den folgenden Code zum Lesen von Zeilen aus einer Datei zu verwenden. Beim Lesen einer Datei befindet sich der Inhalt jedoch alle in einer Zeile:

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

Diese Datei druckt jedoch jede Zeile separat.


Ich muss stdin verwenden, ruby my_prog.rb < file.txtwo ich nicht annehmen kann, welches Zeilenendezeichen die Datei verwendet. Wie kann ich damit umgehen?

zeichnen
quelle
7
Anstatt zu tun line_num = 0, könnten Sie verwenden each.each_with_indexoder möglicherweise each.with_index.
Andrew Grimm
@ andrew-grimm danke, es macht saubereren Code.
Unentschieden
Unter stackoverflow.com/q/25189262/128421 erfahren Sie, warum zeilenweise E / A gegenüber der Verwendung bevorzugt werden read.
der Blechmann
Verwenden Sie line.chomp, um die Zeilenenden zu behandeln (mit freundlicher Genehmigung von @SreenivasanAC )
Yarin

Antworten:

150

Ich glaube , meine Antwort deckt Ihre neue Sorgen über jede Art von Zeilenenden , da sowohl der Handhabung "\r\n"und "\r"werden auf Linux - Standard konvertiert , "\n"bevor die Linien Parsen.

Um den "\r"EOL-Charakter zusammen mit dem regulären "\n"und "\r\n"unter Windows zu unterstützen, würde ich Folgendes tun:

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

Natürlich könnte dies bei sehr großen Dateien eine schlechte Idee sein, da dies bedeutet, dass die gesamte Datei in den Speicher geladen wird.

Olivier L.
quelle
Dieser Regex hat bei mir nicht funktioniert. Das Unix-Format verwendet \ n, Windows \ r \ n, Mac verwendet \ n - .gsub (/ (\ r | \ n) + /, "\ n") hat in allen Fällen für mich funktioniert.
Pod
4
Es sollte eine korrekte Regex sein, /\r?\n/die sowohl \ r \ n als auch \ n
abdeckt,
12
Dadurch wird die gesamte Datei in den Speicher eingelesen. Dies kann je nach Größe der Datei unmöglich sein.
Eremzeit
1
Diese Methode ist sehr ineffizient. Talabes antworten hier. Stackoverflow.com/a/17415655/228589 ist die beste Antwort. Bitte überprüfen Sie die Implementierung dieser beiden Methoden.
CantGetANick
1
Dies ist nicht der rubinrote Weg. Die folgende Antwort zeigt das richtige Verhalten.
Merovex
524

Ruby hat eine Methode dafür:

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

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

Jonathan
quelle
diese methond langsamer als methond die @Olivier L. ist
Hello World
1
@HelloWorld Wahrscheinlich, weil jede vorhergehende Zeile aus dem Speicher gelöscht und in jede Zeile in den Speicher geladen wird. Mag falsch sein, aber Ruby macht die Dinge wahrscheinlich richtig (damit große Dateien nicht zum Absturz Ihres Skripts führen).
Starkers
Können Sie auch damit verwenden with_index?
Joshua Pinter
1
Ja, Sie können, zBFile.readlines(filename).each_with_index { |line, i| puts "#{i}: #{line}" }
Wulftone
Diese Methode scheint besser zu sein. Ich lese sehr große Dateien und auf diese Weise stürzt die Anwendung nicht ab, indem versucht wird, die gesamte Datei auf einmal in den Speicher zu laden.
Shelby S
392
File.foreach(filename).with_index do |line, line_num|
   puts "#{line_num}: #{line}"
end

Dadurch wird der angegebene Block für jede Zeile in der Datei ausgeführt, ohne dass die gesamte Datei in den Speicher verschoben wird. Siehe: IO :: foreach .

Talabes
quelle
10
Dies ist die Antwort - idiomatischer Ruby und schlürft die Datei nicht. Siehe auch stackoverflow.com/a/5546681/165673
Yarin
4
Alle begrüßen die Rubingötter!
Joshua Pinter
Wie gehe ich zur zweiten Zeile innerhalb der Schleife?
user1735921
18

Ihre erste Datei hat Mac Classic-Zeilenenden ( "\r"anstelle der üblichen "\n"). Öffne es mit

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

um die Zeilenenden anzugeben.

Josh Lee
quelle
1
Leider gibt es nichts Vergleichbares wie die universellen Zeilenumbrüche in Python, zumindest die ich kenne.
Josh Lee
Noch eine Frage, ich muss stdin verwenden, wie ruby ​​my_prog.rb <file.txt, wo ich nicht annehmen kann, welches Zeilenende char die Datei verwendet ... Wie kann ich damit umgehen?
Unentschieden
Oliviers Antwort scheint hilfreich zu sein, wenn Sie die gesamte Datei in den Speicher laden können. Das Erkennen von Zeilenumbrüchen beim Scannen der Datei erfordert etwas mehr Arbeit.
Josh Lee
7

Dies liegt an den Endzeilen in den einzelnen Zeilen. Verwenden Sie die Chomp-Methode in Ruby, um die Endzeile '\ n' oder 'r' am Ende zu löschen.

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

Ich bin Teil des folgenden Ansatzes für Dateien mit Headern:

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

Auf diese Weise können Sie eine Kopfzeile (oder Zeilen) anders als die Inhaltszeilen verarbeiten.

Ron Gejman
quelle
6

wie etwa bekommt ?

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

Vergessen Sie nicht, dass Sie die Datei immer lesen können, wenn Sie Bedenken haben, eine Datei einzulesen, die möglicherweise große Zeilen enthält, die Ihren RAM zur Laufzeit überfüllen könnten. Siehe " Warum das Schlürfen einer Datei schlecht ist ".

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
quelle