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?


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.


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


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 .


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.


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

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.


6

wie etwa bekommt ?

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

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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.