protocol buffersバイナリデータを読む
目的
protocol buffersでエンコーディングされたバイナリデータを読んでみます。
準備
この記事では、Protocol Buffer Basics: Pythonのコードを使用します。
addressbook.proto
syntax = "proto2"; package tutorial; 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 phones = 4; } message AddressBook { repeated Person people = 1; }
writing_a_message.py
#! /usr/bin/python import addressbook_pb2 import sys # This function fills in a Person message based on user input. def PromptForAddress(person): person.id = int(raw_input("Enter person ID number: ")) person.name = raw_input("Enter name: ") email = raw_input("Enter email address (blank for none): ") if email != "": person.email = email while True: number = raw_input("Enter a phone number (or leave blank to finish): ") if number == "": break phone_number = person.phones.add() phone_number.number = number type = raw_input("Is this a mobile, home, or work phone? ") if type == "mobile": phone_number.type = addressbook_pb2.Person.MOBILE elif type == "home": phone_number.type = addressbook_pb2.Person.HOME elif type == "work": phone_number.type = addressbook_pb2.Person.WORK else: print "Unknown phone type; leaving as default value." # Main procedure: Reads the entire address book from a file, # adds one person based on user input, then writes it back out to the same # file. if len(sys.argv) != 2: print "Usage:", sys.argv[0], "ADDRESS_BOOK_FILE" sys.exit(-1) address_book = addressbook_pb2.AddressBook() # Read the existing address book. try: f = open(sys.argv[1], "rb") address_book.ParseFromString(f.read()) f.close() except IOError: print sys.argv[1] + ": Could not open file. Creating a new one." # Add an address. PromptForAddress(address_book.people.add()) # Write the new address book back to disk. f = open(sys.argv[1], "wb") f.write(address_book.SerializeToString()) f.close()
reading_a_message.py
#! /usr/bin/python import addressbook_pb2 import sys # Iterates though all people in the AddressBook and prints info about them. def ListPeople(address_book): for person in address_book.people: print "Person ID:", person.id print " Name:", person.name if person.HasField('email'): print " E-mail address:", person.email for phone_number in person.phones: if phone_number.type == addressbook_pb2.Person.MOBILE: print " Mobile phone #: ", elif phone_number.type == addressbook_pb2.Person.HOME: print " Home phone #: ", elif phone_number.type == addressbook_pb2.Person.WORK: print " Work phone #: ", print phone_number.number # Main procedure: Reads the entire address book from a file and prints all # the information inside. if len(sys.argv) != 2: print "Usage:", sys.argv[0], "ADDRESS_BOOK_FILE" sys.exit(-1) address_book = addressbook_pb2.AddressBook() # Read the existing address book. f = open(sys.argv[1], "rb") address_book.ParseFromString(f.read()) f.close() ListPeople(address_book)
解析
以上のコードよりADDRESS_BOOK_FILEを作成します。
% python reading_a_message.py ADDRESS_BOOK_FILE Person ID: 1 Name: jone E-mail address: jone@example.com Mobile phone #: 000-0000-0001 Person ID: 2 Name: mike E-mail address: mike@example.com Home phone #: 000-0000-0002 Work phone #: 000-0000-0022
ADDRESS_BOOK_FILEをhexdumpすると、以下のようになります。
% hexdump -C ADDRESS_BOOK_FILE 00000000 0a 2d 0a 04 6a 6f 6e 65 10 01 1a 10 6a 6f 6e 65 |.-..jone....jone| 00000010 40 65 78 61 6d 70 6c 65 2e 63 6f 6d 22 11 0a 0d |@example.com"...| 00000020 30 30 30 2d 30 30 30 30 2d 30 30 30 31 10 00 0a |000-0000-0001...| 00000030 40 0a 04 6d 69 6b 65 10 02 1a 10 6d 69 6b 65 40 |@..mike....mike@| 00000040 65 78 61 6d 70 6c 65 2e 63 6f 6d 22 11 0a 0d 30 |example.com"...0| 00000050 30 30 2d 30 30 30 30 2d 30 30 30 32 10 01 22 11 |00-0000-0002..".| 00000060 0a 0d 30 30 30 2d 30 30 30 30 2d 30 30 32 32 10 |..000-0000-0022.| 00000070 02 |.| 00000071
ADDRESS_BOOK_FILEをEncodingに従って解析すると以下のようになります。
アドレス | 00000000 | 00000001 | 00000002 | 00000003 | 00000004 | 00000005 | 00000006 | 00000007 |
---|---|---|---|---|---|---|---|---|
データ | 0a |
2d |
0a |
04 |
6a |
6f |
6e |
65 |
内容 | 0x00001010 -> 最後の3ビット(010)よりLength-delimited型, 3回右シフトすると field number = 0x0001(people) | payload size (0x2d bytes) (jone分) | 0x00001010 -> 最後の3ビット(010)よりLength-delimited型, 3回右シフトすると field number = 0x0001(name) | 文字列は4 byte | j | o | n | e |
アドレス | 00000008 | 00000009 | 0000000a | 0000000b | 0000000c | 0000000d | 0000000e | 0000000f |
---|---|---|---|---|---|---|---|---|
データ | 10 |
01 |
1a |
10 |
6a |
6f |
6e |
65 |
内容 | 0x00010000 -> 最後の3ビット(000)よりVarint型, 3回右シフトすると field number = 0x0010(id) | id(jone) = 1 | 0x00011010 -> 最後の3ビット(010)よりLength-delimited型, 3回右シフトすると field number = 0x0011(email) | 文字列は0x10 byte | j | o | n | e |
アドレス | 00000010 | 00000011 | 00000012 | 00000013 | 00000014 | 00000015 | 00000016 | 00000017 |
---|---|---|---|---|---|---|---|---|
データ | 40 |
65 |
78 |
61 |
6d |
70 |
6c |
65 |
内容 | @ | e | x | a | m | p | l | e |
アドレス | 00000018 | 00000019 | 0000001a | 0000001b | 0000001c | 0000001d | 0000001e | 0000001f |
---|---|---|---|---|---|---|---|---|
データ | 2e |
63 |
6f |
6d |
22 |
11 |
0a |
0d |
内容 | . | c | o | m | 0x00100010 -> 最後の3ビット(010)よりLength-delimited型, 3回右シフトすると field number = 0x0100(phones) | payload size (0x11 bytes) | 0x00001010 -> 最後の3ビット(010)よりLength-delimited型, 3回右シフトすると field number = 0x0001(number) | 文字列は0x0d byte |
アドレス | 00000020 | 00000021 | 00000022 | 00000023 | 00000024 | 00000025 | 00000026 | 00000027 |
---|---|---|---|---|---|---|---|---|
データ | 30 |
30 |
30 |
2d |
30 |
30 |
30 |
30 |
内容 | 0 | 0 | 0 | - | 0 | 0 | 0 | 0 |
アドレス | 00000028 | 00000029 | 0000002a | 0000002b | 0000002c | 0000002d | 0000002e | 0000002f |
---|---|---|---|---|---|---|---|---|
データ | 2d |
30 |
30 |
30 |
31 |
10 |
00 |
0a |
内容 | - | 0 | 0 | 0 | 1 | 0x00010000 -> 最後の3ビット(000)よりVarint型, 3回右シフトすると field number = 0x0010(type) | PhoneType = 0(MOBILE) | 0x00001010 -> 最後の3ビット(010)よりLength-delimited型, 3回右シフトすると field number = 0x0001(people) |
アドレス | 00000030 | 00000031 | 00000032 | 00000033 | 00000034 | 00000035 | 00000036 | 00000037 |
---|---|---|---|---|---|---|---|---|
データ | 40 |
0a |
04 |
6d |
69 |
6b |
65 |
10 |
内容 | payload size (0x40 bytes) (mike分) | 0x00001010 -> 最後の3ビット(010)よりLength-delimited型, 3回右シフトすると field number = 0x0001(name) | 文字列は4 byte | m | i | k | e | 0x00010000 -> 最後の3ビット(000)よりVarint型, 3回右シフトすると field number = 0x0010(id) |
アドレス | 00000038 | 00000039 | 0000003a | 0000003b | 0000003c | 0000003d | 0000003e | 0000003f |
---|---|---|---|---|---|---|---|---|
データ | 02 |
1a |
10 |
6d |
69 |
6b |
65 |
40 |
内容 | id(mike) = 2 | 0x00011010 -> 最後の3ビット(010)よりLength-delimited型, 3回右シフトすると field number = 0x0011(email) | 文字列は0x10 byte | m | i | k | e | @ |
アドレス | 00000040 | 00000041 | 00000042 | 00000043 | 00000044 | 00000045 | 00000046 | 00000047 |
---|---|---|---|---|---|---|---|---|
データ | 65 |
78 |
61 |
6d |
70 |
6c |
65 |
2e |
内容 | e | x | a | m | p | l | e | . |
アドレス | 00000048 | 00000049 | 0000004a | 0000004b | 0000004c | 0000004d | 0000004e | 0000004f |
---|---|---|---|---|---|---|---|---|
データ | 63 |
6f |
6d |
22 |
11 |
0a |
0d |
30 |
内容 | c | o | m | 0x00100010 -> 最後の3ビット(010)よりLength-delimited型, 3回右シフトすると field number = 0x0100(phones) | payload size (0x11 bytes) | 0x00001010 -> 最後の3ビット(010)よりLength-delimited型, 3回右シフトすると field number = 0x0001(number) | 文字列は0x0d byte | 0 |
アドレス | 00000050 | 00000051 | 00000052 | 00000053 | 00000054 | 00000055 | 00000056 | 00000057 |
---|---|---|---|---|---|---|---|---|
データ | 30 |
30 |
2d |
30 |
30 |
30 |
30 |
2d |
内容 | 0 | 0 | - | 0 | 0 | 0 | 0 | - |
アドレス | 00000058 | 00000059 | 0000005a | 0000005b | 0000005c | 0000005d | 0000005e | 0000005f |
---|---|---|---|---|---|---|---|---|
データ | 30 |
30 |
30 |
32 |
10 |
01 |
22 |
11 |
内容 | 0 | 0 | 0 | 2 | 0x00010000 -> 最後の3ビット(000)よりVarint型, 3回右シフトすると field number = 0x0010(type) | PhoneType = 1(HOME) | 0x00100010 -> 最後の3ビット(010)よりLength-delimited型, 3回右シフトすると field number = 0x0100(phones) | payload size (0x11 bytes) |
アドレス | 00000060 | 00000061 | 00000062 | 00000063 | 00000064 | 00000065 | 00000066 | 00000067 |
---|---|---|---|---|---|---|---|---|
データ | 0a |
0d |
30 |
30 |
30 |
2d |
30 |
30 |
内容 | 0x00001010 -> 最後の3ビット(010)よりLength-delimited型, 3回右シフトすると field number = 0x0001(number) | 文字列は0x0d byte | 0 | 0 | 0 | - | 0 | 0 |
アドレス | 00000068 | 00000069 | 0000006a | 0000006b | 0000006c | 0000006d | 0000006e | 0000006f |
---|---|---|---|---|---|---|---|---|
データ | 30 |
30 |
2d |
30 |
30 |
32 |
32 |
10 |
内容 | 0 | 0 | - | 0 | 0 | 2 | 2 | 0x00010000 -> 最後の3ビット(000)よりVarint型, 3回右シフトすると field number = 0x0010(type) |
アドレス | 00000070 |
---|---|
データ | 02 |
内容 | PhoneType = 2(WORK) |
バイナリって楽しいですね。