2012-04-02 19 views
6

मैंने huge XML file पर पहले पोस्ट किया था - यह विकिपीडिया डंप के साथ 287 जीबी एक्सएमएल है जिसे मैं सीएसवी फ़ाइल (संशोधन लेखकों और टाइमस्टैम्प) में डाल देना चाहता हूं। मैं कुछ बिंदु तक ऐसा करने में कामयाब रहा। मुझे StackOverflow त्रुटि मिलने से पहले, लेकिन अब पहली समस्या को हल करने के बाद मुझे मिलता है: java.lang.OutOfMemoryError: जावा हीप स्पेस त्रुटि।क्लोजर और जावा हीप स्पेस त्रुटि में बड़ी फ़ाइल

मेरे कोड (आंशिक रूप से जस्टिन क्रेमर जवाब से लिया गया) कि तरह लग रहा है:

(defn process-pages 
    [page] 
    (let [title  (article-title page) 
     revisions (filter #(= :revision (:tag %)) (:content page))] 
    (for [revision revisions] 
     (let [user (revision-user revision) 
      time (revision-timestamp revision)] 
     (spit "files/data.csv" 
       (str "\"" time "\";\"" user "\";\"" title "\"\n") 
       :append true))))) 

(defn open-file 
[file-name] 
(let [rdr (BufferedReader. (FileReader. file-name))] 
    (->> (:content (data.xml/parse rdr :coalescing false)) 
     (filter #(= :page (:tag %))) 
     (map process-pages)))) 

मैं, article-title, revision-user और revision-title कार्यों को नहीं दिखाते क्योंकि वे बस पेज में एक विशिष्ट स्थान से डेटा ले या संशोधन हैश। कोई भी इस के साथ मेरी मदद कर सकता है - मैं क्लोजर में वास्तव में नया हूं और समस्या नहीं पा रहा हूं।

उत्तर

4

लेता है बस स्पष्ट होना, (:content (data.xml/parse rdr :coalescing false)) आलसी है। यदि आप आश्वस्त नहीं हैं तो अपनी कक्षा की जांच करें या पहले आइटम को खींचें (यह तत्काल वापस आ जाएगा)।

उस ने कहा, बड़े अनुक्रमों को संसाधित करते समय देखने के लिए कुछ चीजें: सिर पर पकड़ना, और अवास्तविक/घोंसले आलस्य। मुझे लगता है कि आपका कोड उत्तरार्द्ध से पीड़ित है।

1) कॉल की ->> श्रृंखला के अंत करने के लिए (dorun) जोड़ें:

यहाँ मैं क्या सलाह देते हैं। यह अनुक्रम को सिर पर पकड़े बिना पूरी तरह से महसूस किया जाएगा।

2) forprocess-pagedoseq में बदलें। आप एक फाइल पर थूक रहे हैं, जो एक साइड इफेक्ट है, और आप यहां आलसी नहीं करना चाहते हैं।

जैसा कि आर्थर अनुशंसा करता है, आप प्रत्येक विकिपीडिया प्रविष्टि के लिए & लेखन (थूक) खोलने के बजाय एक बार एक आउटपुट फ़ाइल खोलना और लिखना जारी रखना चाहते हैं।

अद्यतन:

(defn filter-tag [tag xml] 
    (filter #(= tag (:tag %)) xml)) 

;; lazy 
(defn revision-seq [xml] 
    (for [page (filter-tag :page (:content xml)) 
     :let [title (article-title page)] 
     revision (filter-tag :revision (:content page)) 
     :let [user (revision-user revision) 
       time (revision-timestamp revision)]] 
    [time user title])) 

;; eager 
(defn transform [in out] 
    (with-open [r (io/input-stream in) 
       w (io/writer out)] 
    (binding [*out* out] 
     (let [xml (data.xml/parse r :coalescing false)] 
     (doseq [[time user title] (revision-seq xml)] 
      (println (str "\"" time "\";\"" user "\";\"" title "\"\n"))))))) 

(transform "dump.xml" "data.csv") 

मैं कुछ भी यहाँ नहीं दिख रहा है कि अत्यधिक स्मृति उपयोग का कारण होगा:

यहाँ एक पुनर्लेखन जो चिंताओं को अलग करने का प्रयास करता है और अधिक स्पष्ट रूप है।

+1

क्लोरर के लिए नए व्यक्ति के लिए डोरन के बारे में बिंदु थोड़ा स्पष्ट किया जा सकता है: प्रश्न में दिखाए गए खुले फ़ाइल फ़ंक्शन प्रक्रिया-पृष्ठों पर कॉल के परिणामों का अनुक्रम देता है, और जब फ़ंक्शन को प्रतिलिपि से प्रिंट किया जाता है, प्रिंटिंग अनुक्रम सभी परिणामों को एक ही समय में स्मृति में आयोजित करने का कारण बनता है। परिणामस्वरूप डोरन को कॉल करने से अनुक्रम के तत्वों का मूल्यांकन किया जा सकता है और वापस लौटाया जा सकता है, ताकि सभी परिणामों को एक ही समय में स्मृति में रखने की आवश्यकता न हो। स्पष्टीकरण के लिए –

+0

Thanx! मैं समझता हूं (उम्मीद है) अब इस कोड स्निपेट में आलस्य कैसे काम करती है और आपने जो प्रस्तावित किया है उसे बदल दिया है, लेकिन फिर भी 'आउटऑफमेमरी एरर: जावा हीप स्पेस'। मैं अंतिम फ़ाइल के 1 जीबी नमूने पर काम कर रहा हूं, लेकिन यह अभी भी स्मृति त्रुटि को मारता है। किसी भी मदद के लिए वास्तव में आभारी होंगे। – trzewiczek

+0

मेरा नवीनतम अपडेट देखें। अगर आपको अभी भी आउटऑफमेरी त्रुटि मिलती है, तो मुझे यकीन नहीं है कि क्यों। मैंने बिना स्मृति समस्याओं के इस कोड को बहुत समान इस्तेमाल किया। –

1

दुर्भाग्य से data.xml/parse आलसी नहीं है, यह पूरी फ़ाइल को स्मृति में पढ़ने का प्रयास करता है और फिर इसे पार्स करता है।

इसके बजाय this (lazy) xml library का उपयोग करें, जिसमें केवल वर्तमान में यह हिस्सा है जो वर्तमान में राम में काम कर रहा है। इसके बाद आपको आउटपुट लिखने के लिए अपने कोड को पुन: संरचना करने की आवश्यकता होगी क्योंकि यह सभी एक्सएमएल को इकट्ठा करने के बजाय इनपुट को पढ़ता है, फिर इसे आउटपुट करता है।

अपनी लाइन

(:content (data.xml/parse rdr :coalescing false) 

स्मृति में सभी एक्सएमएल लोड होगा और फिर इसे से सामग्री कुंजी का अनुरोध। जो ढेर उड़ जाएगा।

एक आलसी जवाब का एक मोटा रूपरेखा कुछ इस तरह दिखेगा:

(with-open [input (java.io.FileInputStream. "/tmp/foo.xml") 
      output (java.io.FileInputStream. "/tmp/foo.csv"] 
    (map #(write-to-file output %) 
     (filter is-the-tag-i-want? (parse input)))) 

(> data ram) के साथ काम करने धीरज रखो, हमेशा समय :)

+0

वह पहले से ही योगदान से data.xml' उपयोग कर रहा है 'राम को , जैसा कि आप इंगित करते हैं, आलसी है। –

0

मैं Clojure के बारे में पता नहीं है, लेकिन सादे जावा में एक http://docs.oracle.com/javase/1.4.2/docs/api/org/xml/sax/XMLReader.html कि XML लोड करने के लिए की जरूरत नहीं है की तरह एक SAX घटना आधारित पार्सर इस्तेमाल कर सकते हैं