Ruby - чтение файла в пакетном режиме

Я читаю файл размером 10 МБ, который содержит некоторые идентификаторы. Я прочитал их в список в рубине. Я обеспокоен тем, что это может вызвать проблемы с памятью в будущем, когда число идентификаторов в файле может увеличиться. Есть ли эффективный способ чтения большого файла в пакетном режиме?

Спасибо

3 ответа

Решение

Нет универсального пути.

1) вы можете прочитать файл по частям:

File.open('filename','r') do |f|
  chunk = f.read(2048)
  ...
end

недостаток: вы можете пропустить подстроку, если она будет между кусками, то есть вы ищете "SOME_TEXT", но "SOME_" - это последние 5 байтов 1-го 2048-байтового блока, а "TEXT" - это 4 байта 2-го блока

2) Вы можете читать файл построчно

File.open('filename','r') do |f|
  line = f.gets
  ...
end

недостаток: так будет медленнее, чем в первый раз, в 2,5 раза

С помощью Lazy Enumerators и each_slice вы можете получить лучшее из обоих миров. Вам не нужно беспокоиться о разрезании линий в середине, и вы можете перебирать несколько строк в пакете. batch_size можно выбрать свободно.

header_lines = 1
batch_size   = 2000

File.open("big_file") do |file|
  file.lazy.drop(header_lines).each_slice(batch_size) do |lines|
    # do something with batch of lines
  end
end

Его можно использовать для импорта огромного файла CSV в базу данных:

require 'csv'
batch_size   = 2000

File.open("big_data.csv") do |file|
  headers = file.first
  file.lazy.each_slice(batch_size) do |lines|
    csv_rows = CSV.parse(lines.join, write_headers: true, headers: headers)
    # do something with 2000 csv rows, e.g. bulk insert them into a database
  end
end

Если вас так сильно беспокоит скорость / эффективность памяти, рассматривали ли вы возможность использования оболочки в оболочке и использования grep, awk, sedтак далее.? Если бы я знал немного больше о структуре входного файла и о том, что вы пытаетесь извлечь, я мог бы создать для вас команду.

Другие вопросы по тегам