Jak czytać wiersze pliku w Ruby

238

Próbowałem użyć następującego kodu, aby odczytać wiersze z pliku. Ale podczas czytania pliku zawartość jest w jednym wierszu:

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

Ale ten plik drukuje każdą linię osobno.


Muszę użyć stdin, na przykład ruby my_prog.rb < file.txt, gdzie nie mogę założyć, jakiego znaku końca linii używa plik. Jak sobie z tym poradzić?

remis
źródło
7
Zamiast robić line_num = 0, możesz użyć each.each_with_indexlub ewentualnie each.with_index.
Andrew Grimm
@ andrew-grimm dziękuję, sprawia, że ​​kod jest czystszy.
losowanie
Zobacz stackoverflow.com/q/25189262/128421, aby dowiedzieć się, dlaczego IO wiersz po linii jest lepszy niż użycie read.
Tin Man,
Służy line.chompdo obsługi zakończeń linii (dzięki uprzejmości @SreenivasanAC )
Yarin

Odpowiedzi:

150

Wierzę, że moja odpowiedź obejmuje nowe obawy dotyczące obsługi wszelkiego rodzaju zakończeń linii, ponieważ oba "\r\n"i "\r"są przekształcane do standardu Linux "\n"przed analizą linii.

Aby wesprzeć "\r"znak EOL wraz ze zwykłym "\n"i "\r\n"z Windows, oto co bym zrobił:

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

Oczywiście może to być zły pomysł na bardzo duże pliki, ponieważ oznacza to załadowanie całego pliku do pamięci.

Olivier L.
źródło
Ten regex nie działał dla mnie. Format uniksowy używa \ n, Windows \ r \ n, mac używa \ n - .gsub (/ (\ r | \ n) + /, "\ n") działał dla mnie we wszystkich przypadkach.
Pod
4
Prawidłowe wyrażenie regularne powinno /\r?\n/obejmować zarówno \ r \ n, jak i \ n bez łączenia pustych linii, tak jak zrobiłby to komentarz Pod
Irongaze.com
12
Spowoduje to odczytanie całego pliku do pamięci, co może być niemożliwe w zależności od wielkości pliku.
eremzeit,
1
Ta metoda jest bardzo nieefektywna, odpowiedź talabes tutaj stackoverflow.com/a/17415655/228589 jest najlepszą odpowiedzią. Sprawdź wdrożenie tych dwóch metod.
CantGetANick
1
To nie jest rubinowy sposób. Poniższa odpowiedź pokazuje właściwe zachowanie.
Merovex,
525

Ruby ma na to metodę:

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

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

Jonathan
źródło
ten metond wolniej niż metond, który @Olivier L.
HelloWorld,
1
@HelloWorld Prawdopodobnie dlatego, że usuwa każdą poprzednią linię z pamięci i ładuje każdą linię do pamięci. Może się mylić, ale Ruby prawdopodobnie działa poprawnie (aby duże pliki nie powodowały awarii skryptu).
Starkers
Czy możesz with_indexz tym również korzystać?
Joshua Pinter,
1
Tak, możesz np.File.readlines(filename).each_with_index { |line, i| puts "#{i}: #{line}" }
wulftone
Ta metoda wydaje się lepsza. Czytam bardzo duże pliki i w ten sposób nie powoduje to awarii aplikacji, próbując jednocześnie załadować cały plik do pamięci.
Shelby S
393
File.foreach(filename).with_index do |line, line_num|
   puts "#{line_num}: #{line}"
end

Spowoduje to wykonanie danego bloku dla każdej linii w pliku bez umieszczania całego pliku w pamięci. Zobacz: IO :: foreach .

talabes
źródło
10
To jest odpowiedź - idiomatyczny Ruby i nie sluruje pliku. Zobacz także stackoverflow.com/a/5546681/165673
Yarin
4
Wszyscy pozdrawiają Rubinowych bogów!
Joshua Pinter
jak przejść do drugiej linii w pętli?
user1735921,
18

Twój pierwszy plik ma zakończenia linii Mac Classic (to "\r"zamiast zwykłych "\n"). Otwórz za pomocą

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

aby określić zakończenia linii.

Josh Lee
źródło
1
Niestety, nie ma nic takiego jak uniwersalne nowe wiersze w Pythonie, przynajmniej o tym wiem.
Josh Lee
jeszcze jedno pytanie, muszę użyć stdin, np. ruby ​​my_prog.rb <file.txt, gdzie nie mogę założyć, jakiego wiersza kończącego znak char używa plik ... Jak sobie z tym poradzić?
losowanie
Odpowiedź Oliviera wydaje się pomocna, jeśli nie masz nic przeciwko załadowaniu całego pliku do pamięci. Wykrywanie nowych linii podczas skanowania pliku zajmie nieco więcej pracy.
Josh Lee
7

Wynika to z linii końcowych w każdej linii. Użyj metody chomp w ruby, aby usunąć linię końcową „\ n” lub „r” na końcu.

line_num=0
File.open('xxx.txt').each do |line|
  print "#{line_num += 1} #{line.chomp}"
end
Sreenivasan AC
źródło
2
@SreenivisanAC +1 dla chomp!
Yarin
7

Nie zgadzam się z następującym podejściem do plików z nagłówkami:

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

Pozwala to na przetwarzanie linii nagłówka (lub linii) inaczej niż linii treści.

Ron Gejman
źródło
6

jak o robi ?

myFile=File.open("paths_to_file","r")
while(line=myFile.gets)
 //do stuff with line
end
JBoy
źródło
4

Nie zapominaj, że jeśli obawiasz się o odczytanie pliku, który może mieć ogromne linie, które mogłyby zalać twoją pamięć RAM w czasie wykonywania, zawsze możesz odczytać plik kawałek po posiłku. Zobacz „ Dlaczego włamywanie pliku jest złe ”.

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
źródło