2014-10-11 66 views
15

目前即時通訊嘗試使用SAX解析器,但通過文件大約3/4,它只是完全凍結了,我試圖分配更多的內存等,但沒有得到任何改進。如何解析Java中的大(50 GB)XML文件

有什麼辦法可以加快速度嗎?一個更好的方法?

剝離它到裸露的骨頭,所以我現在有下面的代碼,當在命令行運行它仍然不會像我想要的那樣快。

與運行它的 「java -Xms-4096米-Xmx8192m -jar reader.jar」 我避開文章超過70萬

主要爲GC開銷限制:

public class Read { 
    public static void main(String[] args) {  
     pages = XMLManager.getPages(); 
    } 
} 

XMLManager

public class XMLManager { 
    public static ArrayList<Page> getPages() { 

    ArrayList<Page> pages = null; 
    SAXParserFactory factory = SAXParserFactory.newInstance(); 

    try { 

     SAXParser parser = factory.newSAXParser(); 
     File file = new File("..\\enwiki-20140811-pages-articles.xml"); 
     PageHandler pageHandler = new PageHandler(); 

     parser.parse(file, pageHandler); 
     pages = pageHandler.getPages(); 

    } catch (ParserConfigurationException e) { 
     e.printStackTrace(); 
    } catch (SAXException e) { 
     e.printStackTrace(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 


    return pages; 
    }  
} 

PageHandler

public class PageHandler extends DefaultHandler{ 

    private ArrayList<Page> pages = new ArrayList<>(); 
    private Page page; 
    private StringBuilder stringBuilder; 
    private boolean idSet = false; 

    public PageHandler(){ 
     super(); 
    } 

    @Override 
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { 

     stringBuilder = new StringBuilder(); 

     if (qName.equals("page")){ 

      page = new Page(); 
      idSet = false; 

     } else if (qName.equals("redirect")){ 
      if (page != null){ 
       page.setRedirecting(true); 
      } 
     } 
    } 

    @Override 
    public void endElement(String uri, String localName, String qName) throws SAXException { 

     if (page != null && !page.isRedirecting()){ 

      if (qName.equals("title")){ 

       page.setTitle(stringBuilder.toString()); 

      } else if (qName.equals("id")){ 

       if (!idSet){ 

        page.setId(Integer.parseInt(stringBuilder.toString())); 
        idSet = true; 

       } 

      } else if (qName.equals("text")){ 

       String articleText = stringBuilder.toString(); 

       articleText = articleText.replaceAll("(?s)<ref(.+?)</ref>", " "); //remove references 
       articleText = articleText.replaceAll("(?s)\\{\\{(.+?)\\}\\}", " "); //remove links underneath headings 
       articleText = articleText.replaceAll("(?s)==See also==.+", " "); //remove everything after see also 
       articleText = articleText.replaceAll("\\|", " "); //Separate multiple links 
       articleText = articleText.replaceAll("\\n", " "); //remove new lines 
       articleText = articleText.replaceAll("[^a-zA-Z0-9- \\s]", " "); //remove all non alphanumeric except dashes and spaces 
       articleText = articleText.trim().replaceAll(" +", " "); //convert all multiple spaces to 1 space 

       Pattern pattern = Pattern.compile("([\\S]+\\s*){1,75}"); //get first 75 words of text 
       Matcher matcher = pattern.matcher(articleText); 
       matcher.find(); 

       try { 
        page.setSummaryText(matcher.group()); 
       } catch (IllegalStateException se){ 
        page.setSummaryText("None"); 
       } 
       page.setText(articleText); 

      } else if (qName.equals("page")){ 

       pages.add(page); 
       page = null; 

      } 
     } else { 
      page = null; 
     } 
    } 

    @Override 
    public void characters(char[] ch, int start, int length) throws SAXException { 
     stringBuilder.append(ch,start, length); 
    } 

    public ArrayList<Page> getPages() { 
     return pages; 
    } 
} 
+0

你確定什麼是「凍結」(想給我們更多關於你的情況的更多細節)是SAX解析器而不是你的代碼中的東西?您是否將應用程序中的任何對象保存在內存中? – Tim 2014-10-11 04:00:00

+0

目前我只是對它進行一些測試,但我有一種感覺,它可能是日蝕,它凍結了(剝去它的骨頭,它立即凍結)。目前通過命令行運行它,讓你張貼。 – 2014-10-11 05:04:24

+0

增加了一些基本的代碼,只需輸出讀者在xml文件中提供的文章 – 2014-10-11 05:40:01

回答

24

您的解析代碼很可能正常工作,但是您要加載的數據量可能太大而無法保存在ArrayList的內存中。

您需要某種流水線將數據傳送到其實際目標,而無需將其全部存儲在內存中。

我有時爲這種情況所做的事情與以下內容類似。

創建一個接口,用於處理單個元件:

public interface PageProcessor { 
    void process(Page page); 
} 

供應這方面的一個實施到PageHandler通過一個構造:

public class Read { 
    public static void main(String[] args) { 

     XMLManager.load(new PageProcessor() { 
      @Override 
      public void process(Page page) { 
       // Obviously you want to do something other than just printing, 
       // but I don't know what that is... 
       System.out.println(page); 
      } 
     }) ; 
    } 

} 


public class XMLManager { 

    public static void load(PageProcessor processor) { 
     SAXParserFactory factory = SAXParserFactory.newInstance(); 

     try { 

      SAXParser parser = factory.newSAXParser(); 
      File file = new File("pages-articles.xml"); 
      PageHandler pageHandler = new PageHandler(processor); 

      parser.parse(file, pageHandler); 

     } catch (ParserConfigurationException e) { 
      e.printStackTrace(); 
     } catch (SAXException e) { 
      e.printStackTrace(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 

    } 
} 

將數據發送至該處理器,而不是把它在的列表:

public class PageHandler extends DefaultHandler { 

    private final PageProcessor processor; 
    private Page page; 
    private StringBuilder stringBuilder; 
    private boolean idSet = false; 

    public PageHandler(PageProcessor processor) { 
     this.processor = processor; 
    } 

    @Override 
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { 
     //Unchanged from your implementation 
    } 

    @Override 
    public void characters(char[] ch, int start, int length) throws SAXException { 
     //Unchanged from your implementation 
    } 

    @Override 
    public void endElement(String uri, String localName, String qName) throws SAXException { 
      // Elide code not needing change 

      } else if (qName.equals("page")){ 

       processor.process(page); 
       page = null; 

      } 
     } else { 
      page = null; 
     } 
    } 

} 

當然,你可以使您的界面處理多個記錄塊而不是一個,並讓PageHandler在較小的列表中本地收集頁面,並定期將列表發送給處理並清除列表。

或者(也許更好),你可以實現這裏定義的PageProcessor接口,並在那裏構建邏輯,緩存數據並將其發送到塊中進一步處理。

+0

這就是我最終做的,一次10,000頁的效果很好。 – 2014-10-20 03:27:20

+0

我剛開始尋找類似的任務。這是一個自定義頁面類嗎? – Wudang 2017-10-09 16:47:05

0

這確實是問題:pages.add(page);。實際上SAX對內存非常友好,內存使用量不依賴於輸入文件的大小。

我們設計了一個基於XSD生成代碼的代碼生成器(如果可用,或者可以從源文檔生成代碼)。該產品基於SAX,並且無縫地處理多GB文件(我們使用的最大文件是22GB)。它確實遵循了Don Roby在這裏概述的方法。你唯一需要做的就是實現處理器接口。

運行時(Java)的發動機採用了配置文件(Java屬性文件),它允許您訂閱模式類型你有興趣。如果你想有更多這方面的信息,看看這裏:。http://www.xml2java.net/xml-to-java-data-binding-for-big-data/