Как читать строки файла в Ruby


238

Я пытался использовать следующий код для чтения строк из файла. Но при чтении файла содержимое все в одной строке:

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

Но этот файл печатает каждую строку отдельно.


Я должен использовать стандартный ввод, например 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 без объединения пустых строк, как это сделал бы комментарий Пода
Irongaze.com

12
Это прочитает весь файл в память, что может быть невозможно в зависимости от размера файла.
eremzeit

1
Этот метод очень неэффективен, ответ talabes здесь stackoverflow.com/a/17415655/228589 - лучший ответ. Пожалуйста, проверьте реализацию этих двух методов.
CantGetANick

1
Это не рубиновый способ. Ответ ниже показывает правильное поведение.
Меровекс

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

Этот метод кажется лучше. Я читаю очень большие файлы, и таким образом приложение не падает, пытаясь загрузить весь файл в память сразу.
Шелби С

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

Это будет выполнять данный блок для каждой строки в файле, не сохраняя весь файл в памяти. Смотрите: IO :: foreach .


10
Это ответ - идиоматический Ruby и не портит файл. См. Также stackoverflow.com/a/5546681/165673
Ярин

4
Приветствую Рубиновых Богов!
Джошуа Пинтер

как перейти ко второй строке внутри цикла?
user1735921

18

Ваш первый файл имеет окончание строки Mac Classic ( "\r"вместо обычного "\n"). Откройте его

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

указать окончания строки.


1
К сожалению, нет ничего похожего на универсальные переводы строк в Python, по крайней мере, о которых я знаю.
Джош Ли

еще один вопрос, я должен использовать stdin, например, ruby ​​my_prog.rb <file.txt, где я не могу предположить, какой символ конца строки использует файл ... Как я могу справиться с этим?
ничья

Ответ Оливье кажется полезным, если вы согласны с загрузкой всего файла в память. Обнаружение новых строк во время сканирования файла займет немного больше времени.
Джош Ли

7

Это из-за концов в каждой строке. Используйте метод chomp в ruby, чтобы удалить конечную строку '\ 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.