Як читати рядки файлу в Ruby


238

Я намагався використовувати наступний код для читання рядків з файлу. Але при читанні файлу вміст знаходиться в одному рядку:

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

Але цей файл друкує кожен рядок окремо.


Мені доводиться використовувати stdin, наприклад ruby my_prog.rb < file.txt, там, де я не можу припустити, яким символом є кінцевий рядок, який використовує файл. Як я можу це впоратися?


7
Замість того, щоб робити line_num = 0, ви могли б використовувати each.each_with_indexабо можливо each.with_index.
Ендрю Грімм

@ andrew-grimm спасибі, це робить чистіший код.
розіграш

Дивіться stackoverflow.com/q/25189262/128421 для того, чому лінійний рядок вводу-виводу є кращим над використанням read.
Олов'яний чоловік

Використовуйте line.chompдля обробки закінчень рядків (люб’язно надано @SreenivasanAC )
Ярін

Відповіді:


150

Я вважаю, що моя відповідь стосується ваших нових проблем щодо обробки будь-якого типу закінчень рядків, оскільки вони обидва "\r\n"та "\r"переходять у стандарт Linux "\n"перед тим, як аналізувати рядки.

Щоб підтримати "\r"персонаж EOL разом із звичайними "\n"та "\r\n"з Windows, ось що я б робив:

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

Звичайно, це може бути поганою ідеєю для дуже великих файлів, оскільки це означає завантаження всього файлу в пам'ять.


Цей регекс не працював для мене. У форматі Unix використовується \ n, windows \ r \ n, mac використовує \ n - .gsub (/ (\ r | \ n) + /, "\ n") працював для мене у всіх випадках.
Pod

4
Правильний регулярний вираз повинен /\r?\n/охоплювати і \ r \ n, і \ n, не поєднуючи порожні рядки, як це робитиме коментар Pod
Irongaze.com

12
Це прочитає весь файл у пам'яті, що може бути неможливим залежно від того, наскільки великий файл.
eremzeit

1
Цей метод дуже вкрай неефективний, talabes відповісти тут stackoverflow.com/a/17415655/228589 є найкращою відповіддю. Будь ласка, перевірте реалізацію цих двох методів.
CantGetANick

1
Це не рубіновий шлях. Відповідь нижче показує правильну поведінку.
Merovex

525

У Ruby є метод для цього:

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

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


цей метод повільніше, ніж метхонд, що @Olivier L.
HelloWorld

1
@HelloWorld Мабуть тому, що це видалення кожного попереднього рядка з пам'яті та завантаження кожного рядка в пам'ять. Може бути неправильним, але Ruby, ймовірно, робить все належним чином (так що великі файли не спричиняють збій вашого сценарію).
Старкерс

Чи можете ви використовуватись і with_indexз цим?
Джошуа Пінтер

1
Так, ви можете, наприклад,File.readlines(filename).each_with_index { |line, i| puts "#{i}: #{line}" }
wulftone

Цей метод здається кращим. Я читаю дуже великі файли, і таким чином він не виходить з ладу додатком, намагаючись завантажити весь файл в пам'ять відразу.
Shelby S

393
File.foreach(filename).with_index do |line, line_num|
   puts "#{line_num}: #{line}"
end

Це дозволить виконати заданий блок для кожного рядка у файлі, не забиваючи весь файл у пам'ять. Див.: ІО :: передбач .


10
Це відповідь - ідіоматичний Ruby і не базується на файлі. Дивіться також stackoverflow.com/a/5546681/165673
Ярін

4
Усі вітайте богів Рубі!
Джошуа Пінтер

як перейти до другого рядка всередині циклу?
користувач1735921

18

Ваш перший файл має закінчення рядків Mac Classic (це "\r"замість звичайного "\n"). Відкрийте його

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

щоб вказати закінчення рядків.


1
На жаль, у Python немає нічого подібного до універсальних нових рядків, принаймні, що я знаю.
Джош Лі

ще одне запитання: я повинен використовувати stdin, як-от ruby ​​my_prog.rb <file.txt, де я не можу припустити, для чого використовується рядок, що закінчується char-файлом ... Як я можу це обробити?
розіграш

Відповідь Олів'є здається корисною, якщо ви все гаразд із завантаженням у файл всього пам'яті. Виявлення нових рядків під час сканування файлу займе трохи більше роботи.
Джош Лі

7

Це через кінцеві лінії в кожному рядку. Використовуйте метод chomp в рубіні, щоб видалити кінцеву лінію '\ n' або 'r' в кінці.

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

2
@SreenivisanAC +1 для chomp!
Ярін

7

Я відношусь до наступного підходу для файлів із заголовками:

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

Це дозволяє обробляти заголовок (або рядки) інакше, ніж рядки вмісту.



4

Не забувайте, що якщо ви стурбовані читанням у файлі, який може мати величезні рядки, які могли б переповнити оперативну пам’ять під час виконання, ви завжди можете прочитати файл з обробкою. Дивіться " Чому погано прошивання файлу ".

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.