MrZ MrZ - 3 months ago 12
Java Question

wrong output of CRC32 in android

I'm calculating CRC32 like code below :

import java.util.zip.CRC32;


String data = "99D5503012013165411";
byte bytes[] = data.getBytes();
Checksum checksum = new CRC32();
checksum.update(bytes, 0, bytes.length);
long checksumValue = checksum.getValue();
System.out.println("Result of CRC32 : " +Long.toHexString(checksumValue) + " !");


when I compare my code with this online CRC32 calculation it only gives me the right result when the Input type ASCII, So there is a way to have the same result as Hex?

Answer

Your problem is how you create the input (byte bytes[] = ...).

The String#getBytes method returns byte representation of individual characters in the string. But I suppose the input string 99D5503012013165411 is a hex representation of byte array.

So you have to convert it to bytes like here.

One byte is represented by two characters: see Hexadecimal on Wiki.


Update: There is another catch. One hex letter is 4bits, thus it is half of byte. A proper byte array written as hex string should be of even length.

This adds another level of confusion, since your example input is 19 chars long.

Working solution is (that gives output same as the online calc):

  public static void main(String[] args)
  {
    String data = "99D55030120131654101"; // notice the 01 at the end
    byte bytes[] = hexStringToByteArray(data);
    Checksum checksum = new CRC32();
    checksum.update(bytes, 0, bytes.length);
    long checksumValue = checksum.getValue();
    System.out.println("Result of CRC32 : " +Long.toHexString(checksumValue) + " !");
    // prints: Result of CRC32 : 9671cb4a !
  }

  // took from http://stackoverflow.com/a/140861/6749977 :
  public static byte[] hexStringToByteArray(String s) {
    int len = s.length();
    byte[] data = new byte[len / 2];
    for (int i = 0; i < len; i += 2) {
        data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                             + Character.digit(s.charAt(i+1), 16));
    }
    return data;
  }

The online tool you linked handles the missing letter by interpreting the last 1 as 01 (I had to change the input experimentally there). So I had to change the input to be of even length...

If you are going to get really input like this, and you are sure you want to handle the case this way, the hexStringToByteArray procedure should be updated. But be careful, I thing the right way would be to prepend the whole string with zeros to have even-length. Like an base-10 analogy: 132==0123


Update 2: Based on your comment here I add a solution with modified hexStringToByteArray method (even tough I feel a bit like doing your homework for you):

  public static void main(String[] args)
  {
    String data = "99D5503012013165411";
    String dataOnlyHexChars = data.replaceAll("[^0-9a-fA-F]", ""); // not very cool
    byte bytes[] = hexStringToByteArray(dataOnlyHexChars);
    Checksum checksum = new CRC32();
    checksum.update(bytes, 0, bytes.length);
    long checksumValue = checksum.getValue();
    System.out.println("Result of CRC32 : " +Long.toHexString(checksumValue) + " !");
    // prints: Result of CRC32 : 9671cb4a !
  }

  // took from http://stackoverflow.com/a/140861/6749977 and changed a bit :
  public static byte[] hexStringToByteArray(String s) {
    int len = s.length();
    byte[] data = new byte[(len + 1) / 2]; // changed
    for (int i = 0; i < len; i += 2) {
        if (len==i + 1) // changed
          data[i / 2] = (byte) Character.digit(s.charAt(i), 16);
        else
          data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                               + Character.digit(s.charAt(i+1), 16));
    }
    return data;
  }