如何在一个protobuf文件中写入多条消息?

时间:2014-07-21 18:58:07

标签: java protocol-buffers

以google的教程为例:

message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }

  repeated PhoneNumber phone = 4;
}

message AddressBook {
  repeated Person person = 1;
}   

它使用AddressBook包装器消息存储多个Person消息。如果我对其进行序列化或反序列化,则使用AddressBook.mergeFrom(FileInputStream) addressBook.build.().writeTo()

等方法

但如果我有2000万人的记录,它显然会溢出。谷歌也表示,如果我想在不使用包装器消息的情况下将多条消息存储在一个文件中,我需要记录每条消息的长度,这对于字符串类型是不可能的。

是否有一种在一个文件中序列化大量邮件的好方法? 我顺便使用Java。

3 个答案:

答案 0 :(得分:3)

我还没试过这个,但是我希望它可以工作:

写作:

CodedOutputStream output = CodedOutputStream.newInstance(...);

while (...) {
    Person person = ...;
    output.writeMessageNoTag(person);
}

读:

CodedInputStream input = CodedInputStream.newInstance(...);
while (!input.isAtEnd()) {
    Person.Builder builder = Person.newBuilder();
    input.readMessage(builder, null); // Or specify extension registry
    Person person = builder.build();
    // Use person
}

答案 1 :(得分:0)

在Java中,您可以使用writeDelimitedToparseDelimitedFrom方法来编写和读取相同类型的多条消息。

来自MessageLite documentation

  

writeTo(OutputStream)类似,但在写入数据之前将消息的大小写为varint。这允许在消息之后将更多数据写入流,而无需自己分隔消息数据。使用MessageLite.Builder.mergeDelimitedFrom(InputStream)(或静态方法YourMessageType.parseDelimitedFrom(InputStream))来解析此方法写入的消息。

示例:

// writing
List<Person> addressBook = ...; // list of person to be stored
try(FileOutputStream output = new FileOutputStream(path)) {
  for (Person person: addressBook) {
    person.writeDelimitedTo(output); 
  }
}

// reading
try (FileInputStream input = new FileInputStream(path)) {
  while (true) {
    Person person = Person.parseDelimitedFrom(input);
    if (person == null) { // parseDelimitedFrom returns null on EOF
      break;
    }

    // use person
  }
}

答案 2 :(得分:0)

关键是以追加模式打开文件。

FileOutputStream output = new FileOutputStream(file, true);

因此,以下将是完整的解决方案。

// writing
List<Person> addressBook = ...; // list of person to be stored
try(FileOutputStream output = new FileOutputStream(path, true)) {
  for (Person person: addressBook) {
    person.writeDelimitedTo(output); 
  }
}

// reading
try (FileInputStream input = new FileInputStream(path)) {
  while (true) {
    Person person = Person.parseDelimitedFrom(input);
    if (person == null) { // parseDelimitedFrom returns null on EOF
      break;
    }

    // use person
  }
}

希望这可以节省一些人的时间。