Как декодировать двоичный ответ protobuf - PullRequest
5 голосов
/ 27 октября 2011

Я создал тестовое приложение, которое может распознавать некоторые изображения с помощью Goggle Goggles. У меня это работает, но я получаю бинарный ответ protobuf. У меня нет прото-файлов, только бинарный ответ. Как я могу получить данные из него? (Прислали картинку с бутылкой медведя и получили ответ от nex):

A
TuborgLogo9 HoaniText���;�)b���2d8e991bff16229f6"�
+TR=T=AQBd6Cl4Kd8:X=OqSEi:S=_rSozFBgfKt5d9b0
+TR=T=6rLQxKE2xdA:X=OqSEi:S=gd6Aqb28X0ltBU9V
+TR=T=uGPf9zJDWe0:X=OqSEi:S=32zTfdIOdI6kuUTa
+TR=T=RLkVoGVd92I:X=OqSEi:S=P7yOhvSAOQW6SRHN
+TR=T=J1FMvNmcyMk:X=OqSEi:S=5Z631_rd2ijo_iuf�

нужно получить строку "Tuborg" и по возможности набрать - "Logo"

Ответы [ 3 ]

10 голосов
/ 03 марта 2015

Вы можете декодировать с помощью protoc:

protoc --decode_raw < msg.bin
3 голосов
/ 19 февраля 2018
UnknownFieldSet.parseFrom(msg).toString()

Это покажет вам поля верхнего уровня.К сожалению, он не может знать точные детали типов полей.long / int / bool / enum и т. д. все они закодированы как Varint и выглядят одинаково.Строки, байтовые массивы и вложенные сообщения разделяются по длине и также неразличимы.

Некоторые полезные детали здесь: https://github.com/dcodeIO/protobuf.js/wiki/How-to-reverse-engineer-a-buffer-by-hand

Если вы будете следовать коду в UnknownFieldSet.mergeFrom (), вы увидите, как вы можете попытаться декодировать вложенные сообщения и вернуться к строкам, еслиэто терпит неудачу - но это не будет очень надежно.

В протоколе есть 2 запасных значения для wiretype - было бы действительно полезно, если бы Google использовал одно из них для обозначения вложенных сообщений.(И, возможно, другое для нулевых значений.)

Вот некоторый очень грубый поспешный код, который пытается произвести что-то полезное для диагностики.Он догадывается о типах данных, а в случае строк и вложенных сообщений он печатает обе альтернативы в некоторых случаях.Пожалуйста, не доверяйте никаким значениям, которые он печатает:

public static String decodeProto(byte[] data, boolean singleLine) throws IOException {
    return decodeProto(ByteString.copyFrom(data), 0, singleLine);
}

public static String decodeProto(ByteString data, int depth, boolean singleLine) throws IOException {
    final CodedInputStream input = CodedInputStream.newInstance(data.asReadOnlyByteBuffer());
    return decodeProtoInput(input, depth, singleLine);
}

private static String decodeProtoInput(CodedInputStream input, int depth, boolean singleLine) throws IOException {
    StringBuilder s = new StringBuilder("{ ");
    boolean foundFields = false;
    while (true) {
        final int tag = input.readTag();
        int type = WireFormat.getTagWireType(tag);
        if (tag == 0 || type == WireFormat.WIRETYPE_END_GROUP) {
            break;
        }
        foundFields = true;
        protoNewline(depth, s, singleLine);

        final int number = WireFormat.getTagFieldNumber(tag);
        s.append(number).append(": ");

        switch (type) {
            case WireFormat.WIRETYPE_VARINT:
                s.append(input.readInt64());
                break;
            case WireFormat.WIRETYPE_FIXED64:
                s.append(Double.longBitsToDouble(input.readFixed64()));
                break;
            case WireFormat.WIRETYPE_LENGTH_DELIMITED:
                ByteString data = input.readBytes();
                try {
                    String submessage = decodeProto(data, depth + 1, singleLine);
                    if (data.size() < 30) {
                        boolean probablyString = true;
                        String str = new String(data.toByteArray(), Charsets.UTF_8);
                        for (char c : str.toCharArray()) {
                            if (c < '\n') {
                                probablyString = false;
                                break;
                            }
                        }
                        if (probablyString) {
                            s.append("\"").append(str).append("\" ");
                        }
                    }
                    s.append(submessage);
                } catch (IOException e) {
                    s.append('"').append(new String(data.toByteArray())).append('"');
                }
            break;
            case WireFormat.WIRETYPE_START_GROUP:
                s.append(decodeProtoInput(input, depth + 1, singleLine));
                break;
            case WireFormat.WIRETYPE_FIXED32:
                s.append(Float.intBitsToFloat(input.readFixed32()));
                break;
            default:
                throw new InvalidProtocolBufferException("Invalid wire type");
        }

    }
    if (foundFields) {
        protoNewline(depth - 1, s, singleLine);
    }
    return s.append('}').toString();
}

private static void protoNewline(int depth, StringBuilder s, boolean noNewline) {
    if (noNewline) {
        s.append(" ");
        return;
    }
    s.append('\n');
    for (int i = 0; i <= depth; i++) {
        s.append(INDENT);
    }
}
2 голосов
/ 21 апреля 2012

Я собираюсь предположить, что реальный вопрос заключается в том, как декодировать protobufs, а не в том, как читать двоичный файл с провода, используя Java.

Ответ на ваш вопрос можно найти здесь

Вкратце, на проводе protobufs кодируются как 3 кортежа из <ключ, тип, значение>, где ключ - это номер поля, назначенный полю в схеме .proto. Тип является одним из . Он содержит достаточно информации для декодирования значения 3-го кортежа, а именно говорит, как долго это значение.

...