如何在Java中解析大的(50GB)XML文件
目前,我正在尝试使用SAX解析器,但大约3/4文件完全冻结,我尝试分配更多内存等,但没有得到任何改进 有没有办法加快速度?更好的方法 把它剥离到了最底层,所以我现在有了下面的代码,当在命令行中运行时,它仍然没有我想要的那么快 使用“java-Xms-4096m-Xmx8192m-jarreader.jar”运行它,我得到的GC开销限制超过了大约700000条 主要内容: XMLManager如何在Java中解析大的(50GB)XML文件,java,xml,xml-parsing,sax,Java,Xml,Xml Parsing,Sax,目前,我正在尝试使用SAX解析器,但大约3/4文件完全冻结,我尝试分配更多内存等,但没有得到任何改进 有没有办法加快速度?更好的方法 把它剥离到了最底层,所以我现在有了下面的代码,当在命令行中运行时,它仍然没有我想要的那么快 使用“java-Xms-4096m-Xmx8192m-jarreader.jar”运行它,我得到的GC开销限制超过了大约700000条 主要内容: XMLManager public class XMLManager { public static ArrayLis
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;
}
}
公共类XMLManager{
公共静态ArrayList getPages(){
arraylistpages=null;
SAXParserFactory=SAXParserFactory.newInstance();
试一试{
SAXParser parser=factory.newSAXParser();
File File=新文件(“..\\enwiki-20140811-pages-articles.xml”);
PageHandler PageHandler=新的PageHandler();
parser.parse(文件,页面处理程序);
pages=pageHandler.getPages();
}捕获(ParserConfiguration异常e){
e、 printStackTrace();
}捕获(SAXE异常){
e、 printStackTrace();
}捕获(IOE异常){
e、 printStackTrace();
}
返回页面;
}
}
页面处理程序
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;
}
}
公共类PageHandler扩展了DefaultHandler{
private ArrayList pages=new ArrayList();
私人网页;
私人StringBuilder StringBuilder;
私有布尔idSet=false;
公共页面处理程序(){
超级();
}
@凌驾
public void startElement(字符串uri、字符串localName、字符串qName、属性)引发SAXException{
stringBuilder=新的stringBuilder();
如果(qName.equals(“第页”)){
页面=新页面();
idSet=false;
}else if(qName.equals(“重定向”)){
如果(第页!=null){
页面设置重定向(真);
}
}
}
@凌驾
public void endElement(字符串uri、字符串localName、字符串qName)引发SAXException{
if(page!=null&&!page.isRedecting()){
如果(qName.equals(“标题”)){
page.setTitle(stringBuilder.toString());
}else if(qName.equals(“id”)){
如果(!idSet){
setId(Integer.parseInt(stringBuilder.toString());
idSet=true;
}
}else if(qName.equals(“text”)){
String articleText=stringBuilder.toString();
articleText=articleText.replaceAll((?s)您的解析代码可能工作正常,但加载的数据量可能太大,无法保存在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
接口,并在其中内置逻辑,缓冲数据并将其发送到块中进行进一步处理。Don Roby的方法与我创建代码生成器以解决此特定问题的方法有些相似(早期版本是在2008年构思的)。基本上,每个complexType
都有其Java POJO
等价物,当上下文更改到该元素时,特定类型的处理程序被激活。我在SEPA、交易银行和例如discogs(30GB)中使用了这种方法。您可以使用属性文件声明性地指定要在运行时处理的元素
XML2J一方面使用complexTypes
到Java POJO的映射,但允许您指定要侦听的事件。
例如
本质在第三行。分离确保单个帐户不会添加到帐户列表中。因此不会溢出
class AccountType {
private List<AccountType> accounts = new ArrayList<>();
public void addAccount(AccountType tAccount) {
accounts.add(tAccount);
}
// etc.
};
请注意,XMLEvent.END
标记元素的结束标记。因此,当您处理它时,它是完整的。如果必须关联它(使用FK)对于其在数据库中的父对象,您可以处理XMLEvent.BEGIN
对于父对象,在数据库中创建一个占位符,并使用其键与其每个子对象一起存储。在最后的XMLEvent.END
中,您将更新父对象
请注意,代码生成器生成您需要的所有内容。您只需实现该方法,当然还有DB glue代码
代码生成器甚至会生成POM文件,因此您可以在生成后立即构建项目
默认的处理方法如下所示:
@Override
public void process(XMLEvent evt, ComplexDataType data)
throws ProcessorException {
/*
* TODO Auto-generated method stub implement your own handling here.
* Use the runtime configuration file to determine which events are to be sent to the processor.
*/
if (evt == XMLEvent.END) {
data.print( ConsoleWriter.out );
}
}
下载:
首先mvn clean安装
核心(必须在本地maven repo中),然后是生成器。别忘了按照用户手册中的说明设置环境变量XML2J_HOME
。您确定什么是“冻结”(想给我们更多关于这对您的情况意味着什么的详细信息吗?)是SAX解析器而不是代码中的某个东西吗?您是否将对象保留在应用程序的任何内存中?目前我只是在对其运行一些测试,但我有一种感觉,可能是eclipse冻结了(将其剥离到裸骨,然后它就冻结了)。目前通过命令行运行它,让您
class AccountType {
private List<AccountType> accounts = new ArrayList<>();
public void addAccount(AccountType tAccount) {
accounts.add(tAccount);
}
// etc.
};
class AccountsProcessor implements MessageProcessor {
static private Logger logger = LoggerFactory.getLogger(AccountsProcessor.class);
// assuming Spring data persistency here
final String path = new ClassPathResource("spring-config.xml").getPath();
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(path);
AccountsTypeRepo repo = context.getBean(AccountsTypeRepo.class);
@Override
public void process(XMLEvent evt, ComplexDataType data)
throws ProcessorException {
if (evt == XMLEvent.END) {
if( data instanceof AccountType) {
process((AccountType)data);
}
}
}
private void process(AccountType data) {
if (logger.isInfoEnabled()) {
// do some logging
}
repo.save(data);
}
}
@Override
public void process(XMLEvent evt, ComplexDataType data)
throws ProcessorException {
/*
* TODO Auto-generated method stub implement your own handling here.
* Use the runtime configuration file to determine which events are to be sent to the processor.
*/
if (evt == XMLEvent.END) {
data.print( ConsoleWriter.out );
}
}