Wie zu Analysieren Groß (50 GB) in XML-Dateien in Java
Derzeit im Versuch, verwenden Sie einen SAX-Parser aber über 3/4 durch die Datei, die es einfach komplett einfriert, habe ich versucht, die Zuweisung von mehr Speicher usw, aber nicht immer Verbesserungen.
Gibt es eine Möglichkeit, um diese Fahrt? Eine bessere Methode?
Machten ihn zum bloßen Knochen, also ich habe jetzt den folgenden code, und bei der Ausführung in der Befehlszeile es immer noch nicht gehen so schnell, wie ich es gerne hätte.
Läuft es mit "java -Xms-4096m -Xmx8192m -jar reader.jar" ich bekomme einen GC overhead limit exceeded um Artikel 700000
Main:
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;
}
}
InformationsquelleAutor der Frage Joe Maher | 2014-10-11
Du musst angemeldet sein, um einen Kommentar abzugeben.
Parser-code ist wahrscheinlich in Ordnung arbeiten, aber das Volumen der Daten, die Sie laden, ist wohl einfach zu groß, um zu halten in Erinnerung, dass
ArrayList
.Müssen Sie irgendeine Art von pipeline übergeben Sie die Daten auf, um seiner eigentlichen Bestimmung, ohne jemals
speichern Sie alle im Speicher auf einmal.
Was ich schon manchmal gemacht für diese Art von situation, die der folgenden ähnlich ist.
Erstellen Sie eine Schnittstelle für die Verarbeitung eines einzelnen Elements:
Versorgung eine Umsetzung dieser auf die
PageHandler
durch einen Konstruktor:Senden von Daten zu diesem Prozessor, anstatt es in die Liste:
Natürlich, Sie können Ihre Oberfläche behandeln Stücke von mehreren Datensätzen und nicht nur eins und haben die
PageHandler
sammeln von Webseiten lokal in einer kleineren Liste und senden regelmäßig die Liste für die Bearbeitung und löschen Sie die Liste.Oder (vielleicht besser), könnten Sie implementieren die
PageProcessor
- Schnittstelle, wie hier definiert, und bauen in der Logik gibt, puffert die Daten und sendet Sie zur weiteren Behandlung in Blöcken.InformationsquelleAutor der Antwort Don Roby
Don Roby Ansatz ist etwas erinnert an den Ansatz, dem ich gefolgt Erstellung eines code-Generators zur Lösung dieses speziellen Problems (eine frühe version war konzipiert im Jahr 2008). Im Grunde genommen jeder
complexType
hat seineJava POJO
entspricht und Prozeduren für die Besondere Art aktiviert werden, wenn sich der Kontext ändert, dass dieses element. Ich habe diesen Ansatz für den SEPA-Zahlungsverkehr und zum Beispiel discogs (30GB). Sie können festlegen, welche Elemente, die Sie verarbeiten möchten, zur Laufzeit, deklarativ mit einem propeties Datei.XML2J verwendet mapping von
complexTypes
Java POJOs, die auf der einen Seite, aber Sie können Ereignisse festlegen, die Sie hören möchten.E. g.
Das Wesen ist in der Dritten Zeile. Das trennen stellt sicher, dass die einzelnen Konten nicht mehr auf der Liste der accounts. So ist es nicht überlaufen.
In Ihrem code müssen Sie den Prozess zu implementieren-Methode (standardmäßig der code-generator erzeugt eine leere Methode:
Beachten Sie, dass
XMLEvent.END
markiert das Ende-tag eines Elements. Also, wenn Sie verarbeiten es, ist es komplett. Wenn Sie haben, Sie beziehen sich (mit einem FK) zu seinem übergeordneten Objekt in der Datenbank, Sie konnte denXMLEvent.BEGIN
für die Eltern, erstellen Sie einen Platzhalter in der Datenbank, und verwenden Sie Ihre Schlüssel zu speichern, mit jedem seiner Kinder. In der letztenXMLEvent.END
Sie würde dann aktualisieren Sie die Eltern.Beachten Sie, dass der code-generator erzeugt alles, was Sie brauchen. Sie müssen nur umsetzen, die Methode und der Kurs der DB glue-code.
Gibt es Proben, um Ihnen den Einstieg. Der code-generator generiert sogar Ihre POM-Dateien, so können Sie sofort nach der Generierung erstellen Sie Ihr Projekt.
Den Standard-Prozess-Methode ist wie folgt:
Downloads:
Ersten
mvn clean install
den Kern (es muss in das lokale maven-repo), dann den generator. Und vergessen Sie nicht zum einrichten der UmgebungsvariablenXML2J_HOME
wie pro Anweisungen in der usermanual.InformationsquelleAutor der Antwort dexter