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ć?


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.


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


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


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.


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

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.


6

jak o robi ?

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

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.