使用 VTD-XML 将大 XML 文件分成小块时出现异常
Exception when split big XML file in small chunks with VTD-XML
我正在开发一个小程序,可以将一个非常大的 XML 文件(超过 2Gb)分成小块。
研究了很多库后,我选择了VTD-XML(对大文件使用VTDGenHuge),开始开发一些代码测试。但是当我读取文件的段字节时遇到问题。
我得到偏移量和长度:
long [] l = vn.getElementFragment();
然后得到result的信息:
int offset = (int) (l[0] >> 64);
int len = new Integer("" + l[1]);
最后我尝试提取字节段并将其写入另一个文件:
b = new byte[len];
fis.read(b, offset, len); **//<===== this is the exception problem**
但我得到 java.lang.IndexOutOfBoundsException
此外,当我为字节数组分配一个固定数字时(例如新字节[400]],程序结束正常,但输出文件已损坏。
我的代码:
File fo = new File("\path\post_people.xml");
FileOutputStream fos = new FileOutputStream(fo);
int count = 0;
File f = new File("\path\people.xml");
FileInputStream fis = new FileInputStream(f);
byte[] b;
VTDGenHuge vg = new VTDGenHuge();
if (vg.parseFile("\path\people.xml", false, VTDGenHuge.MEM_MAPPED)){
VTDNavHuge vn = vg.getNav();
AutoPilotHuge ap = new AutoPilotHuge();
ap.bind(vn);
ap.selectXPath("/people/person"); //here it could be posible add another condition
while (ap.evalXPath() != -1) {
long [] l = vn.getElementFragment();
int offset = (int) (l[0] >> 64);
int len = new Integer("" + l[1]);
b = new byte[len];
fis.read(b, offset, len); //<===== this is the line problem
fos.write(b); // writing the fragment out into other file
count++;
if (count == 3) { //this is just a test
break;
}
}
}
XML 文件样本:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<people>
<person>
<name>Nombre 0</name>
<lastName>ApPaterno 1</lastName>
<birthdate>2017-11-10T10:20:44.926-05:00</birthdate>
<age>0</age>
<address>
<streetType>Tipo Calle 0</streetType>
<streetName>Nombre de Calle 0</streetName>
<number>0</number>
</address>
</person>
<person>
<name>Nombre 1</name>
<lastName>ApPaterno 1</lastName>
<birthdate>2017-11-10T10:20:44.926-05:00</birthdate>
<age>1</age>
<address>
<streetType>Tipo Calle 1</streetType>
<streetName>Nombre de Calle 1</streetName>
<number>1</number>
</address>
</person>
</people>
伙计们,你们能帮帮我吗?
更新和解决方案:
最后,我要修改的片段代码如下:
long [] l = vn.getElementFragment();
int offset = (int) (l[0] >> 64);
int len = new Integer("" + l[1]);
b = new byte[len];
fis.getChannel().position(0); //must return to position 0
fis.skip(offset); //must move to offset position
fis.read(b, 0, len);
正如您所指出的,代码中的主要问题在于输入流的读取范围内:
int offset = (int) (l[0] >> 64);
int len = new Integer("" + l[1]);
b = new byte[len];
fis.read(b, offset, len);
根据 InputStream.read() 的 JavaDoc:
The first byte read is stored into element b[off], the next one into b[off+1], and so on.
这意味着您的实际缓冲区必须是长度为 offset + len,这会将字节 0 保留为 0,或者您跳过输入流的第一个 offset 字节并将 len 字节读入缓冲区从位置 0 开始填充缓冲区。
如果把上面的代码换成
int offset = (int) (l[0] >> 64);
int len = new Integer("" + l[1]);
b = new byte[len];
fis.skip(offset);
fis.read(b, 0, len);
缓冲区应填充
的实际字符串表示形式的字节
<person>
<name>Nombre 0</name>
<lastName>ApPaterno 1</lastName>
<birthdate>2017-11-10T10:20:44.926-05:00</birthdate>
<age>0</age>
<address>
<streetType>Tipo Calle 0</streetType>
<streetName>Nombre de Calle 0</streetName>
<number>0</number>
</address>
</person>
我正在开发一个小程序,可以将一个非常大的 XML 文件(超过 2Gb)分成小块。
研究了很多库后,我选择了VTD-XML(对大文件使用VTDGenHuge),开始开发一些代码测试。但是当我读取文件的段字节时遇到问题。
我得到偏移量和长度:
long [] l = vn.getElementFragment();
然后得到result的信息:
int offset = (int) (l[0] >> 64);
int len = new Integer("" + l[1]);
最后我尝试提取字节段并将其写入另一个文件:
b = new byte[len];
fis.read(b, offset, len); **//<===== this is the exception problem**
但我得到 java.lang.IndexOutOfBoundsException
此外,当我为字节数组分配一个固定数字时(例如新字节[400]],程序结束正常,但输出文件已损坏。
我的代码:
File fo = new File("\path\post_people.xml");
FileOutputStream fos = new FileOutputStream(fo);
int count = 0;
File f = new File("\path\people.xml");
FileInputStream fis = new FileInputStream(f);
byte[] b;
VTDGenHuge vg = new VTDGenHuge();
if (vg.parseFile("\path\people.xml", false, VTDGenHuge.MEM_MAPPED)){
VTDNavHuge vn = vg.getNav();
AutoPilotHuge ap = new AutoPilotHuge();
ap.bind(vn);
ap.selectXPath("/people/person"); //here it could be posible add another condition
while (ap.evalXPath() != -1) {
long [] l = vn.getElementFragment();
int offset = (int) (l[0] >> 64);
int len = new Integer("" + l[1]);
b = new byte[len];
fis.read(b, offset, len); //<===== this is the line problem
fos.write(b); // writing the fragment out into other file
count++;
if (count == 3) { //this is just a test
break;
}
}
}
XML 文件样本:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<people>
<person>
<name>Nombre 0</name>
<lastName>ApPaterno 1</lastName>
<birthdate>2017-11-10T10:20:44.926-05:00</birthdate>
<age>0</age>
<address>
<streetType>Tipo Calle 0</streetType>
<streetName>Nombre de Calle 0</streetName>
<number>0</number>
</address>
</person>
<person>
<name>Nombre 1</name>
<lastName>ApPaterno 1</lastName>
<birthdate>2017-11-10T10:20:44.926-05:00</birthdate>
<age>1</age>
<address>
<streetType>Tipo Calle 1</streetType>
<streetName>Nombre de Calle 1</streetName>
<number>1</number>
</address>
</person>
</people>
伙计们,你们能帮帮我吗?
更新和解决方案:
最后,我要修改的片段代码如下:
long [] l = vn.getElementFragment();
int offset = (int) (l[0] >> 64);
int len = new Integer("" + l[1]);
b = new byte[len];
fis.getChannel().position(0); //must return to position 0
fis.skip(offset); //must move to offset position
fis.read(b, 0, len);
正如您所指出的,代码中的主要问题在于输入流的读取范围内:
int offset = (int) (l[0] >> 64);
int len = new Integer("" + l[1]);
b = new byte[len];
fis.read(b, offset, len);
根据 InputStream.read() 的 JavaDoc:
The first byte read is stored into element b[off], the next one into b[off+1], and so on.
这意味着您的实际缓冲区必须是长度为 offset + len,这会将字节 0 保留为 0,或者您跳过输入流的第一个 offset 字节并将 len 字节读入缓冲区从位置 0 开始填充缓冲区。
如果把上面的代码换成
int offset = (int) (l[0] >> 64);
int len = new Integer("" + l[1]);
b = new byte[len];
fis.skip(offset);
fis.read(b, 0, len);
缓冲区应填充
的实际字符串表示形式的字节<person>
<name>Nombre 0</name>
<lastName>ApPaterno 1</lastName>
<birthdate>2017-11-10T10:20:44.926-05:00</birthdate>
<age>0</age>
<address>
<streetType>Tipo Calle 0</streetType>
<streetName>Nombre de Calle 0</streetName>
<number>0</number>
</address>
</person>