2010-01-04 3 views
4

Ruby에서 libxml을 사용하는 수백만 개의 작은 서지 레코드 (예 : <article>...</article>)가 포함 된 큰 XML 파일을 읽고 싶습니다. 레코드로 레코드를 읽는 expand 메서드와 함께 Reader 클래스를 시도했지만 코드가 메모리를 차지하기 때문에 이것이 올바른 접근 방법인지 확신 할 수 없습니다. 따라서 일정한 메모리 사용으로 레코드별로 편리하게 레코드를 처리하는 방법을 찾고 있습니다. 여기청크로 libxml-ruby 청크로 대용량 XML 파일 처리

File.open('dblp.xml') do |io| 
     dblp = XML::Reader.io(io, :options => XML::Reader::SUBST_ENTITIES) 
     pubFactory = PubFactory.new 

     i = 0 
     while dblp.read do 
     case dblp.name 
      when 'article', 'inproceedings', 'book': 
      pub = pubFactory.create(dblp.expand) 
      i += 1 
      puts pub 
      pub = nil 
      $stderr.puts i if i % 10000 == 0 
      dblp.next 
      when 'proceedings','incollection', 'phdthesis', 'mastersthesis': 
      # ignore for now 
      dblp.next 
      else 
      # nothing 
     end 
     end 
    end 

의 핵심은 dblp.expand은 (AN <article> 기록처럼) 전체 서브 트리를 읽고 추가 처리를 위해 공장에 대한 인수로 전달하는 것입니다 : 아래 내 주요 루프입니다. 이것이 올바른 접근 방법입니까?

팩토리 메서드 내에서 다음과 같이 높은 수준의 XPath 식을 사용하여 아래와 같이 요소의 내용을 추출합니다. 다시 말하지만, 이것이 가능한가?

def first(root, node) 
    x = root.find(node).first 
    x ? x.content : nil 
end 

pub.pages = first(node,'pages') # node contains expanded node from dblp.expand 
+0

약간의 후속 조치 : x86의 OS X 10.6 및 x86의 Debian Linux에서 Ruby 1.8.7을 더 테스트 한 후에 XML 파일을 읽는 동안 두 시스템에서 seg faults가 발생했습니다. 버그가 libxml-ruby에서 유래 한 것 같지만 지금까지 추적하지 않았습니다. 실망 스럽네. –

+0

https://github.com/amolpujari/reading-huge-xml –

답변

5

큰 XML 파일을 처리 할 때, 당신은 메모리의 모든 내용을로드 피하기 위해 스트림 파서를 사용합니다. 일반적인 두 가지 방법이 있습니다

  • 푸시 파서 당신이 그들을 얻을로 태그를 encoutered에 반응 SAX 같은이 (tadman에게 대답을 참조).
  • 풀 파서 당신이 같은 간단한 기본 요소로 이동할 수있는 XML 파일에 "커서를"제어는 등 내려 가서/

그 푸시 파서 경우에 사용하는 것이 좋은 생각 당신은 단지 일부 필드를 가져오고 싶지만 일반적으로 복잡한 데이터 추출에 사용하기가 지저분하고 종종 awith를 사용하여 구현됩니다.

풀 서술자는 제 생각에 나무 기반 모델과 푸시 분석기 사이의 좋은 대안입니다 . Dr. Dobb의 저널에서 REXML로 파서를 가져 오는 것에 대한 nice article을 찾을 수 있습니다.

+0

포인터 주셔서 감사. 'XML :: Reader'는 실제로'next'를 사용하여 진행되고'expand'를 사용하여 전체 하위 트리를 읽을 수있는 커서를 기반으로하는 풀 (pull) 파서입니다. 내 코드는 메모리가 누수된다는 점을 제외하고는 작동하지만 큰 파일에서 사용하는 방법에 대한 기본적인 오해가 원인이라고 생각됩니다. 모든 XML :: Reader 전문가가 의견을 남기고 싶습니까? –

1

XML을 처리 할 때 두 가지 공통 옵션은 트리 기반 및 이벤트 기반입니다. 트리 기반 접근 방식은 일반적으로 전체 XML 문서를 읽고 많은 양의 메모리를 소비 할 수 있습니다. 이벤트 기반 접근 방식은 추가 메모리를 사용하지 않지만 사용자가 직접 처리기 로직을 ​​작성하지 않는 한 아무 작업도 수행하지 않습니다.

이벤트 기반 모델은 SAX 스타일 구문 분석기 및 파생 구현에서 사용됩니다. REXML와

예 : http://www.iro.umontreal.ca/~lapalme/ForestInsteadOfTheTrees/HTML/ch08s01.html

REXML : http://ruby-doc.org/stdlib/libdoc/rexml/rdoc/index.html

+1

트리 기반 대 스트림 기반 파싱을 알고 있습니다. API 문서에 따르면 XML :: Reader는 스트림을 파싱하고 커서를 모델링합니다. 후자는 '다음'과 '확장'으로 앞당겨집니다. 그러나 문서에는 큰 파일 용으로 사용하는 좋은 예가 없습니다. –

+1

예는 항상 문제입니다. 예. 나는 나무 기반의 파서를 선호하지만, 보통 사용하기가 훨씬 쉽지만 이런 경우에는 좀 더 SAXy를 사용하여 붙어있다. 좋은 소식은 SAX 메소드를 기반으로 작성된 많은 Java 코드 예제가 Ruby에 상당히 이식 가능하다는 것입니다. 그러나 패러다임처럼 보이는 것이 더 나은 해결책을 가지고 있습니다. – tadman

0

나는 같은 문제가 있었지만 Node # remove를 호출하여 해결했다고 생각합니다! 확장 된 노드에서. 귀하의 경우, 나는이 작품 왜 당신이 정말로 확실하지

 
my_node = dblp.expand 
[do what you have to do with my_node] 
dblp.next 
my_node.remove! 

같은 뭔가를해야한다고 생각하지만 LibXML의 소스 :: XML :: 리더 # 확장 보면, 노드를 해제에 대한 의견이있다 . Reader # expand가 노드를 Reader에 연결한다는 것을 추측하고 Node # remove를 호출해야합니다! 그것을 해방시키기 위해서.

메모리 사용량은이 해킹으로도 좋지 않았지만, 적어도 계속 증가하지는 않았습니다.

+1

감사. 내가 추리하는 동안 그것은 여전히 ​​나를 위해 작동하지 않습니다. 그러나 확장을 사용하지 않고 다음에 호출하는 루프에서 파일을 읽는 것이 효과적입니다. 확장 메서드에서 메모리 누수가 의심됩니다. –

관련 문제