Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/xml/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby 使用SaxMachine解析大文件似乎是将整个文件加载到内存中_Ruby_Xml_Nokogiri_Sax_Large Files - Fatal编程技术网

Ruby 使用SaxMachine解析大文件似乎是将整个文件加载到内存中

Ruby 使用SaxMachine解析大文件似乎是将整个文件加载到内存中,ruby,xml,nokogiri,sax,large-files,Ruby,Xml,Nokogiri,Sax,Large Files,我有一个1.6gb的xml文件,当我用Sax机器解析它时,它看起来并不是在流式传输或成片地吃掉文件,而是似乎在将整个文件加载到内存中(或者可能在某个地方有内存泄漏?),因为我的ruby进程的ram超过了2.5gb。我不知道它在哪里停止生长,因为我的内存用完了 在较小的文件(50mb)上,它似乎也正在加载整个文件。我的任务迭代xml文件中的记录,并将每条记录保存到数据库中。大约需要30秒的“空闲”,然后数据库查询突然开始执行 我认为SAX应该允许您处理这样的大文件,而无需将整个文件加载到内存中 有

我有一个1.6gb的xml文件,当我用Sax机器解析它时,它看起来并不是在流式传输或成片地吃掉文件,而是似乎在将整个文件加载到内存中(或者可能在某个地方有内存泄漏?),因为我的ruby进程的ram超过了2.5gb。我不知道它在哪里停止生长,因为我的内存用完了

在较小的文件(50mb)上,它似乎也正在加载整个文件。我的任务迭代xml文件中的记录,并将每条记录保存到数据库中。大约需要30秒的“空闲”,然后数据库查询突然开始执行

我认为SAX应该允许您处理这样的大文件,而无需将整个文件加载到内存中

有什么东西是我忽略的吗

非常感谢

更新以添加代码示例

class FeedImporter

  class FeedListing
    include ::SAXMachine

    element :id
    element :title
    element :description
    element :url

    def to_hash
      {}.tap do |hash|
        self.class.column_names.each do |key|
          hash[key] = send(key)
        end
      end
    end
  end

  class Feed
    include ::SAXMachine
    elements :listing, :as => :listings, :class => FeedListing
  end

  def perform
    open('~/feeds/large_feed.xml') do |file|

      # I think that SAXMachine is trying to load All of the listing elements into this one ruby object.
      puts 'Parsing'
      feed = Feed.parse(file)

      # We are now iterating over each of the listing elements, but they have been "parsed" from the feed already.
      puts 'Importing'
      feed.listings.each do |listing|
        Listing.import(listing.to_hash)
      end

    end
  end

end
如您所见,我不关心提要中的
元素。我只需要每个
元素的属性

输出如下所示:

Parsing
... wait forever
Importing (actually, I don't ever see this on the big file (1.6gb) because too much memory is used :(
gem 'sax-machine', :git => 'https://github.com/gregwebs/sax-machine'

你说得对,机器急切地阅读整个文档。请查看它的处理程序源:


为了解决您的问题,我将直接使用并自己实现处理程序。

这里有一个阅读器,它将把每个列表的XML生成一个块,这样您就可以处理每个列表,而无需将整个文档加载到内存中

reader = Nokogiri::XML::Reader(file)
while reader.read
  if reader.node_type == Nokogiri::XML::Reader::TYPE_ELEMENT and reader.name == 'listing'
    listing = FeedListing.parse(reader.outer_xml)
    Listing.import(listing.to_hash)
  end
end
如果列表元素可以嵌套,并且希望将最外层的列表作为单个文档进行解析,则可以执行以下操作:

require 'rubygems'
require 'nokogiri'


# Monkey-patch Nokogiri to make this easier
class Nokogiri::XML::Reader
  def element?
    node_type == TYPE_ELEMENT
  end

  def end_element?
    node_type == TYPE_END_ELEMENT
  end

  def opens?(name)
    element? && self.name == name
  end

  def closes?(name)
    (end_element? && self.name == name) || 
      (self_closing? && opens?(name))
  end

  def skip_until_close
    raise "node must be TYPE_ELEMENT" unless element?
    name_to_close = self.name

    if self_closing?
      # DONE!
    else
      level = 1
      while read
        level += 1 if opens?(name_to_close)
        level -= 1 if closes?(name_to_close)

        return if level == 0
      end
    end
  end

  def each_outer_xml(name, &block)
    while read
      if opens?(name)
        yield(outer_xml)
        skip_until_close
      end
    end
  end

end
一旦对其进行了修补,就很容易单独处理每个列表:

open('~/feeds/large_feed.xml') do |file|
  reader = Nokogiri::XML::Reader(file)
  reader.each_outer_xml('listing') do |outer_xml|

    listing = FeedListing.parse(outer_xml)
    Listing.import(listing.to_hash)

  end
end

我将sax机器分叉,以便它使用恒定内存:

好消息:有一个新的维护人员正在计划合并我的更改。
我和新的维护人员已经一年没有问题地使用我的fork了。

不幸的是,现在sax机器有问题了。更糟糕的是,gemspec版本没有被碰撞

尽管有评论,但我认为这段代码并没有集成到pauldix或ezkl的forks中。要使用基于光纤的懒惰版本的代码,我认为您需要在您的GEM文件中明确引用以下版本:

Parsing
... wait forever
Importing (actually, I don't ever see this on the big file (1.6gb) because too much memory is used :(
gem 'sax-machine', :git => 'https://github.com/gregwebs/sax-machine'

对你的问题的简单回答是:是的,有些东西你忽略了。不幸的是你没有告诉我们这是什么。没有人能在他们看不见的代码中找到内存泄漏。@MichaelKay我添加了一个示例。谢谢你证实我的怀疑。可惜sax机器不做懒惰的评估,也不提供真正的回调机制——那太棒了。太棒了,工作得非常好。这似乎也很快,因为本地机器上的数据库成为导入的瓶颈。谢谢你,约翰!我能够使用这种方法和规范的sax机器gem解析我的大型xml文档。谢谢看来你是对的。Github网络图()显示Greg的更改尚未合并到规范SAXMachine repo(由pauldix维护)中。该分支似乎与规范存储库不同步,并且已经两年没有被触及。它还抛出了根纤维屈服的错误…我也得到了“(纤维错误)不能从根纤维屈服”的错误,看起来这个分支已经被放弃了。