【问题标题】:Parsing variable record lengths in Preon在 Preon 中解析可变记录长度
【发布时间】:2010-03-02 10:25:51
【问题描述】:

我正在尝试使用Preon 解析二进制文件,这些文件被构造为一系列可变长度记录。对于每条记录,都有一个数字指定记录长度(以字节为单位)。

这是我正在尝试做的简化版本:

package test.preon; 

import nl.flotsam.preon.annotation.BoundList; 
import nl.flotsam.preon.annotation.BoundNumber; 
import java.util.List; 

public class BinFile { 
    @BoundNumber(size="16") int numberOfRecords; 
    @BoundList(type=Record.class, size="numberOfRecords") List<Record> records; 

    public int getNumberOfRecords() { 
        return numberOfRecords;
    } 

    public List<Record> getRecords() { 
        return records;
    } 

    public class Record { 
        @BoundNumber(size="16") int recordLength; 
        @BoundList(size="recordLength") byte[] data; 

        public int getRecordLength() { 
            return recordLength; 
        } 

        public byte[] getData() { 
            return data; 
        } 
    } 
}

所以,numberOfRecords 指定文件中的记录数,recordLength 指定每条记录的长度。问题是 Preon 无法解析 Record 中的 recordLength,尽管 numberOfRecords 在 BinFile 中可以正常工作。

这是我得到的例外:

nl.flotsam.limbo.BindingException: Failed to resolve recordLength on class test.preon.BinFile
at nl.flotsam.preon.codec.BindingsContext$BindingsResolver.get(BindingsContext.java:412)
at nl.flotsam.preon.codec.BindingsContext$BindingReference.resolve(BindingsContext.java:247)
at nl.flotsam.preon.codec.BindingsContext$BindingReference.resolve(BindingsContext.java:189)
at nl.flotsam.limbo.ast.ReferenceNode.eval(ReferenceNode.java:57)
at nl.flotsam.limbo.ast.ArithmeticNode$Operator$5.eval(ArithmeticNode.java:109)
at nl.flotsam.limbo.ast.ArithmeticNode.eval(ArithmeticNode.java:250)
at nl.flotsam.limbo.ast.ArithmeticNode.eval(ArithmeticNode.java:33)
at nl.flotsam.limbo.ast.ArithmeticNode$Operator$3.eval(ArithmeticNode.java:83)
at nl.flotsam.limbo.ast.ArithmeticNode.eval(ArithmeticNode.java:250)
at nl.flotsam.limbo.ast.ArithmeticNode.eval(ArithmeticNode.java:33)
at nl.flotsam.limbo.ast.ArithmeticNode$Operator$5.eval(ArithmeticNode.java:109)
at nl.flotsam.limbo.ast.ArithmeticNode.eval(ArithmeticNode.java:250)
at nl.flotsam.limbo.ast.ArithmeticNode.eval(ArithmeticNode.java:33)
at nl.flotsam.preon.codec.ListCodecFactory$SwitchingListCodec.decode(ListCodecFactory.java:458)
at nl.flotsam.preon.codec.ListCodecFactory$SwitchingListCodec.decode(ListCodecFactory.java:443)
at nl.flotsam.preon.binding.StandardBindingFactory$FieldBinding.load(StandardBindingFactory.java:128)
at nl.flotsam.preon.codec.ObjectCodecFactory$ObjectCodec.decode(ObjectCodecFactory.java:251)
at nl.flotsam.preon.DefaultCodecFactory$DefaultCodec.decode(DefaultCodecFactory.java:173)
at nl.flotsam.preon.Codecs.decode(Codecs.java:218)
at nl.flotsam.preon.Codecs.decode(Codecs.java:199)
    ...

如果我将 size="recordLength" 更改为常数,例如size="42",我没有得到异常(但是当然,记录长度必须始终相同)。

我是否有其他方法可以使记录长度可变,或者我应该以不同的方式组织事物?

如果有人感兴趣,这是我使用的 JUnit 测试:

package test.preon;

import org.junit.Test;
import static org.junit.Assert.*;
import nl.flotsam.preon.Codecs;
import nl.flotsam.preon.Codec;
import nl.flotsam.preon.DecodingException;
import test.preon.BinFile;
import test.preon.BinFile.Record;
import java.util.List;

public class BinFileTest {

    @Test
    public void parseBinFile() throws DecodingException {
        Codec<BinFile> codec = Codecs.create(BinFile.class);
        byte[] buffer = new byte[] {
                2, 0, 
                3, 0, 
                'a', 'b', 'c',
                4, 0,
                '1', '2', '3', '4'
        };
        BinFile b = Codecs.decode(codec, buffer);

        assertEquals(b.getNumberOfRecords(), 2);

        List<Record> rL = b.getRecords();

        assertEquals(rL.size(), 2);

        Record r0 = rL.get(0);
        assertEquals(r0.getRecordLength(), 3);
        assertEquals(new String(r0.getData()), "abc");

        Record r1 = rL.get(1);
        assertEquals(r1.getRecordLength(), 4);
        assertEquals(new String(r1.getData()), "1234");
    }
}

【问题讨论】:

    标签: java parsing preon


    【解决方案1】:

    原来你遇到了一个错误。 ListCodecFactory 有一个策略来决定在各种情况下生成哪种类型的编解码器,结果在这种情况下它选择了错误的编解码器。我有它的补丁,如果你有兴趣我可以发给你。

    【讨论】:

    • 为该问题附加了一个补丁。
    【解决方案2】:

    为此创建了两个错误报告:PREON-16 和 PREON-17。第一个解决了上述问题。第二个解决了一个稍微相关的问题,这里值得一提:

    在下面的代码中,名为“记录”的列表中的 Test2 元素的大小完全由 Test1 定义。 (Test2的大小基本上就是Test2.value中的字符数,由Test1的'nrCharacters'属性决定。)

    因此,Preon 能够优化读取记录列表。它不必一次读取所有记录;相反,它能够跳过它并仅在需要时读取这些元素。 (元素的起始位置基本上是元素索引的函数。)

    然而,困难在于需要在读取 Test2 的实例之前计算元素的大小。由于它包含基于 Test2 上下文的引用,因此需要重写这些引用。事实上,整个尺寸表达式(尽管在本例中是一个简单的)都需要重写。这在PREON-17 中得到了解决。

     public static class Test1 {
    
            @BoundNumber(size = "8")
            public int nrRecords;
    
            @BoundNumber(size = "8")
            public int nrCharacters;
    
            @BoundList(size = "nrRecords", type = Test2.class)
            public List<Test2> records;
    
            public static class Test2 {
    
                @BoundString(size = "outer.nrCharacters")
                public String value;
    
            }
    
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多