代码之家  ›  专栏  ›  技术社区  ›  Susan Inga

使用VTD-XML将大XML文件拆分为小块时出现异常

  •  2
  • Susan Inga  · 技术社区  · 8 年前

    我正在开发一个小程序,将一个非常大的XML文件(超过2Gb)分成小块。

    在研究了许多库之后,我选择了VTD-XML(对大文件使用VTDGenHuge),并开始开发一些代码测试。但是当我读取文件的段字节时,我遇到了一个问题。

    我得到偏移和长度:

                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 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);
    
    1 回复  |  直到 8 年前
        1
  •  1
  •   Roman Vottner    8 年前

    正如您所指出的,代码中的主要问题是在inputstream的读取过程中:

    int offset = (int) (l[0] >> 64);
    int len = new Integer("" + l[1]);
    b = new byte[len];
    fis.read(b, offset, len);
    

    根据 InputStream.read()

    读取的第一个字节存储在元素b[off]中,下一个字节存储在元素b[off+1]中,依此类推。

    这意味着您的实际缓冲区必须是lenght offset+len,这将0到offset的字节保留为0,或者您跳过输入流的第一个偏移字节,并通过从位置0开始填充缓冲区将len字节读取到缓冲区中。

    如果将上述代码替换为

    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>