raydac/java-binary-block-parser

How to read/parse stringj when here is no '3' mark?

ramunasjurgilas opened this issue · 3 comments

I am trying to parse binary file which begins with string (3 chars) with null termination.

The problem is, that this library requires to have prefix '3' before string, but in my binary file here is not this type of prefix.

My question how to parse string when here is no '3' prefix in front of string?

Failing error:

2021-11-09 12:50:22.141 11264-11264/com.example.binaryparser E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.binaryparser, PID: 11264
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.binaryparser/com.example.binaryparser.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'com.igormaznitsa.jbbp.model.JBBPAbstractField com.igormaznitsa.jbbp.model.JBBPFieldStruct.findFieldForType(java.lang.Class)' on a null object reference

Binary file structure is:

    let prefix: String
    let checksum: UInt32
    let version: ECGFileVersion
    let reserved: UInt8
    let channelMask: UInt16
    let sampleCount: UInt32
    let frequency: UInt32
    let reference: Int32
    let gains: [Int32]
    var samples: [Int] = [Int]()

To pars file prefix using this code:

        final JBBPParser parser = JBBPParser.prepare("stringj;");
        JBBPFieldStruct result = null;
        try {
            result = parser.parse(data);
        } catch (IOException e) {
            e.printStackTrace();
        }
        String dd = result.findFieldForType(JBBPFieldString.class).getAsString();

I have binary file like this:
image

stringj is not null-ended format, if you have some specific data type thenm you can just provide new data type or my pascal asc ii reader from test

final class AscIIPascalString implements JBBPCustomFieldTypeProcessor {
      private final String[] TYPES = new String[] {"asciistr"};

      @Override
      public String[] getCustomFieldTypes() {
        return TYPES;
      }

      @Override
      public boolean isAllowed(
          final JBBPFieldTypeParameterContainer fieldType,
          final String fieldName,
          final int extraData,
          final boolean isArray
      ) {
        return extraData == 0;
      }

      @Override
      public JBBPAbstractField readCustomFieldType(
          final JBBPBitInputStream in,
          final JBBPBitOrder bitOrder,
          final int parserFlags,
          final JBBPFieldTypeParameterContainer customTypeFieldInfo,
          final JBBPNamedFieldInfo fieldName,
          final int extraData,
          final boolean readWholeStream,
          final int arrayLength
      ) throws IOException {
        if (arrayLength < 0) {
          return new JBBPFieldString(fieldName, readPascalAscIIString(in));
        } else {
          final String[] loadedStrings;
          if (readWholeStream) {
            final List<String> strings = new ArrayList<>();
            while (in.hasAvailableData()) {
              strings.add(readPascalAscIIString(in));
            }
            loadedStrings = strings.toArray(new String[0]);
          } else {
            loadedStrings = new String[arrayLength];
            for (int i = 0; i < arrayLength; i++) {
              loadedStrings[i] = readPascalAscIIString(in);
            }
          }
          return new JBBPFieldArrayString(fieldName, loadedStrings);
        }
      }

      private String readPascalAscIIString(final JBBPBitInputStream in) throws IOException {
        final byte[] charArray = in.readByteArray(in.readByte());
        return new String(charArray, StandardCharsets.US_ASCII);
      }
    }

it can be used final JBBPParser parserSingle = JBBPParser.prepare("asciistr str1; asciistr str2;", new AscIIPascalString());

did it help?

It is too much code for parsing null terminated string. Started using ByteBuffer and it is more simple. Here is Kotlin extension for doing it:

fun ByteBuffer.getString(): String {
    var byteArrayStream = ByteArrayOutputStream()
    while (hasRemaining()) {
        var byte: Byte = get()
        if (byte.toInt() == 0x0) {
            break
        }
        byteArrayStream.write(byte.toInt())
    }
    return String(byteArrayStream.toByteArray())
}