在Ruby中无法分配内存(没有MemoryError)?

6
我写了一个简单的脚本,它应该能够读取整个目录并将HTML数据解析成普通脚本,通过去掉HTML标签,然后将其写入一个文件中。
我有8GB内存,也有足够的虚拟内存可用。当我执行此操作时,有超过5GB的可用RAM。目录中最大的文件大小为3.8GB。
这个脚本是:
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 GB)。这里的问题是什么?即使我有足够的内存,为什么还会出现这个错误?我该如何修复它?


失败的文件有多大?它是否含有换行符? - Patru
首先添加日志以查看它是在特定文件或一段时间后失败。如果它总是在同一个文件上失败,尝试在只包含此文件的文件夹上运行它。这将为您提供有价值的信息。 - Martin
@ Patru,谢谢你提醒我,我更新了我的回答。它是最大的一个。 - Sarp Kaya
@Martin,如我所说,它是最高的那一个,我单独尝试了一下,得到了相同的错误。 - Sarp Kaya
“++counter” 不是有效的 Ruby 代码(至少它不会做你想象中的那样)。 - matt
@matt 我的错,后来我使用了 counter += 1 因为 ++counter 不起作用,但是忘记删除 :) - Sarp Kaya
1个回答

11

问题出在这两行代码:

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

当你读取大文件时,你会在内存中慢慢地创建一个非常大的字符串。

最简单的解决方案是根本不使用这个临时的source字符串,而是直接将结果写入文件。只需用以下代码替换那两行即可:

# source << tmp_src
out1.write(tmp_src) 

# source << tmp_src.gsub( /\s{2,}/, "\n" )
out1.write(tmp_src.gsub( /\s{2,}/, "\n" ))                     
这样做可以避免在内存中创建任何大型临时字符串,应该能更好地工作(并更快)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接