【问题标题】:How to read multiple protobufs from one file in java?如何从java中的一个文件中读取多个protobuf?
【发布时间】:2015-04-16 20:25:35
【问题描述】:

我有一个文件“test.txt”,其中包含每行用testMessage.writeDelimitedTo(the-DataOutputStream-that uses a new FileOutputStream pointing to the file) 编写的多个protobuf“TestMessage”消息。如何读取 test.txt 的每一行并取回每行的 protobuf?

在包含字符串的文件上使用缓冲读取器,我会这样做:

String strLine; // What is the alternative to String?
    while ((strLine = br.readLine()) != null)   {
         System.out.println (strLine);
         TestMessage test = new TestMessage.builder();
         test.parseDelimitedFrom(strLine);
    }

如果我要执行此方法,我应该将类型设置为什么而不是“String”?这可能吗?

或者我可以不这样做并且每个消息都必须写入单独的文件吗?

注意:假设 TestMessage 是唯一的消息。

【问题讨论】:

  • 您需要使用 Java Protocol Buffers API。它不是文本文件,您不应该尝试逐行阅读。
  • 所以你说如果我有 10 条消息我想保存在一个文件中以供以后阅读,我应该使用单独的文件来代替吗?
  • @Rolando 不一定。正如我在回答中所描述的,您可以为文件中存储的每个对象创建自己的标头。如果您没有时间或没有意愿创建自己的标题,那么可以。您必须为每个对象使用单独的文件。
  • 那么如果我在 protobuf 消息上使用“writeDelimitedTo”,我如何找出要存储的长度?还是我不应该一开始就使用 writeDelimitedTo 函数?
  • 此外,您必须拥有并使用实际消息结构的匹配协议定义:没有这个,就没有真正的方法来访问编码数据。您不一定需要使用生成的 Java 对象(取决于库),但任何读取 protobuf 编码数据的东西都需要基于 protoc 定义。

标签: java protocol-buffers


【解决方案1】:

为什么要每行写每条消息?我认为您可以只使用 writeDelimitedTo,然后消息可以一一写入。而且阅读非常简单。

User user = User.newBuilder().setUid(1).build();
User user2 = User.newBuilder().setUid(2).build();
try {
    FileOutputStream output = new FileOutputStream(path);
    user.writeDelimitedTo(output);
    user.writeDelimitedTo(output);
    user2.writeDelimitedTo(output);
    output.close();
} catch (Exception e) {
    System.out.print("Write error!");
}

try {
    FileInputStream input = new FileInputStream(path);
    while (true) {
        User user_ = User.parseDelimitedFrom(input);
        if (user_ == null)
            break;
        System.out.println("read from file: \n" + user_);
    }
} catch (Exception e) {
    System.out.println("Read error!");
}

【讨论】:

  • 你应该更多地解释你的答案。仅仅发布可行的代码并不能帮助其他有类似问题的人。
【解决方案2】:

Protobuf 与行分隔的文本文件没有太多共同之处。 Protobuf 用于将对象分解为字节。这个过程称为序列化。 Protobuf 尤其注重兼容性和小尺寸。

您遇到的问题是 protobuf 不存储有关每个对象由多少字节组成或每个对象是什么类型的信息。因此,如果您将许多 protobuf 序列化对象存储到一个文件中,则无法在不包含有关要遵循的对象类型以及该对象由多少字节组成的数据的情况下提取它们。

此数据称为标头。

public void serializeProtobufObject(OutputStream stream, Object obj){
    byte[] bytes = getProtobufBytes(obj);
    int id = getObjectID(obj);

    //write protobuf header info
    writeInt(stream,id);
    writeInt(stream,bytes.length);

    //write protobuf payload
    stream.write(bytes,0,bytes.length);
}

//called repeatedly for many objects in the same stream.
public Object deserializeProtobufObject(InputStream stream){
    //read protobuf header
    int id = readInt(stream);
    int length = readInt(stream);

    //use header to interpret payload
    return readObject(id, length, stream);
}

一个整数 ID 会告诉你后面是什么类型的对象。整数长度告诉您对象由多少字节组成。反序列化时,您将使用这两条信息来提取 protobuf 对象。如果同一流中有许多 protobuf 对象,您将重复执行此操作。

这里的一种更好的方法是为这两个字段创建一个 Protobuf 对象,然后像这样将对象序列化到您的流中:

ProtobufHeader for Foo
[Foo]
ProtobufHeader for Bar
[Bar]

这将允许您在未来扩展您的 protobuf 标头。

【讨论】:

  • “readObject”是如何工作的?因为你不能逐行阅读它接缝。每个文件都“附加”到我正在使用的一个文件的新行中。
  • ReadObject 从流中提取 length 字节,并尝试使用 protobuf 的读取例程读取映射到整数类型 id 的对象。还抛弃了新行的概念,因为它是某种带有 protobuf 的分隔符。新行在 protobuf 中没有任何意义。
  • 我希望能够以每行文本的形式写出字节,然后将它们读回对象中。
  • 这很危险,因为您的序列化 protobuf 对象可能包含换行符。
  • 我明白了。您能否进一步详细说明您的示例,特别是 readObject?我无法看到它如何与一遍又一遍地调用 parseDelimitedFrom 匹配。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-03-29
  • 2020-03-31
  • 2012-10-23
  • 2020-12-25
  • 1970-01-01
  • 1970-01-01
  • 2015-08-18
相关资源
最近更新 更多