Не удалось выделить память (без MemoryError) в Ruby?

Я написал простой скрипт, который должен прочитать весь каталог, а затем проанализировать данные HTML в обычный скрипт, избавившись от тегов HTML, а затем записать его в один файл.

у меня есть память 8GB, а также много доступной виртуальной памяти. Когда я делаю это, у меня есть более 5 ГБ оперативной памяти. Самый большой файл в каталоге-3,8 ГБ.

скрипт

file_count = 1
File.open("allscraped.txt", 'w') do |out1|
    for file_name in Dir["allParts/*.dat"] do
        puts "#{file_name}#:#{file_count}"
        file_count +=1
        File.open(file_name, "r") do |file|
            source = ""
            tmp_src = ""
            counter = 0
            file.each_line do |line|
                scraped_content = line.gsub(/<.*?\/?>/, '')
                tmp_src << scraped_content
                if (counter % 10000) == 0
                    tmp_src = tmp_src.gsub( /\s{2,}/, "\n" )
                    source << tmp_src
                    tmp_src = ""
                    counter = 0
                end
                counter += 1
            end
            source << tmp_src.gsub( /\s{2,}/, "\n" )
            out1.write(source)
            break
        end
    end
end

полный код ошибки:

realscraper.rb:33:in `block (4 levels) in <main>': failed to allocate memory (No
MemoryError)
        from realscraper.rb:27:in `each_line'
        from realscraper.rb:27:in `block (3 levels) in <main>'
        from realscraper.rb:23:in `open'
        from realscraper.rb:23:in `block (2 levels) in <main>'
        from realscraper.rb:13:in `each'
        from realscraper.rb:13:in `block in <main>'
        from realscraper.rb:12:in `open'
        from realscraper.rb:12:in `<main>'

где линия № 27 составляет file.each_line do |line| и 33 составляет source << tmp_src. Файл с ошибкой является самым большим (3,8 ГБ). В чем проблема? Почему я получаю эту ошибку хотя у меня достаточно памяти? Также как я могу это исправить?

1 ответов


проблема в этих двух строках:

source << tmp_src
source << tmp_src.gsub( /\s{2,}/, "\n" )

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

самое простое решение-не использовать этот временный source string вообще, но записывать результаты непосредственно в файл. Просто замените эти две строки следующим:

# source << tmp_src
out1.write(tmp_src) 

# source << tmp_src.gsub( /\s{2,}/, "\n" )
out1.write(tmp_src.gsub( /\s{2,}/, "\n" ))                     

таким образом, вы не создаете больших временных строк в памяти, и он должен работать лучше (и быстрее) таким образом.