Convert Ascii value directly to Hex value - java

I have a literal String made of the hexadecimal alphabet such as 00bff040dadd1fde55 and I wish to convert the string into its hex 'equivalent' in bytes i.e. into a byte array such as {0x00 0xBF 0xF0 0x40 0xDA 0xDD 0x1F 0xDE 0x55}
Is there a Java class that can do this conversion? If not, does anyone have an algorithm I can implement to do the above conversion?

Is there a Java class that can do this conversion?
There is no class in the standard library that will convert a string containing hex characters into an array of bytes ... like that1.
If not, does anyone have an algorithm I can implement to do the above conversion?
Divide length of the string by 2, and allocate byte array of that size.
Repeat until string is empty:
Remove a substring comprising the first two characters.
Use Integer.parseInt(twoChars, 16) to convert to an integer.
Cast the integer to byte and add to byte array.
This algorithm can easily be converted to Java code.
1 - If you actually want a sequence of byte "literals" represented as a string, then look at #aetheria's answer ...

If you mean you want to separate the string into bytes of hex, you can use a regular expression:
String s = "00bff040dadd1fde55";
String hex = "{" + s.toUpperCase().replaceAll("(..)", "0x$1 ").trim() + "}";
System.out.println(hex);
Prints:
{0x00 0xBF 0xF0 0x40 0xDA 0xDD 0x1F 0xDE 0x55}
If you mean you want a byte array to result:
int size = s.length() / 2;
byte[] arr = new byte[size];
for (int i = 0; i < size; i++) {
arr[i] = (byte) Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16);
}
To prove the array contains the right bytes:
for (int i = 0; i < arr.length; i++) {
System.out.print(String.format("%02X ", arr[i]) + " ");
}
Prints:
00 BF F0 40 DA DD 1F DE 55

Related

Binary Representation to a new byte

I'm trying to create a new byte knowing a certain amount of bits
char prostie1 = theRepChars[j-3];
char prostie2 = theRepChars[j-2];
char prostie3 = theRepChars[j-1];
char prostie4 = theRepChars[j];
String prostiaMare = prostie4 + prostie3 + prostie2 + prostie1 + "";
Byte theChar = new Byte(prostiaMare);
When i do this I get a NumberFormatException value 196.
I have no idea what might be my problem
--EDIT--
Ok I think I might have to give some more details since I wasn't very clear. I'm trying to do an Uuencode algorithm and by following the logic of the algorithm I should stop my byte having a value bigger than 194. Here is a bunch of my code.
if(my_chars.length % 3 == 0)
{
for(int x = 0; x < my_chars.length; x++)
{
if((x+1) % 3 == 0)
{
char first = my_chars[x-2];
char second = my_chars[x-1];
char third = my_chars[x];
int n = (((first << 8) | second) << 8) | third;
String theRep = Integer.toBinaryString(n);
while(theRep.length() < 24 - 1)
{
theRep = 0 + theRep;
}
//0 padded theRep
for(int j = 0; j < theRepChars.length; j++)
{
if((j+1) % 4 == 0)
{
char prostie1 = theRepChars[j-3];
char prostie2 = theRepChars[j-2];
char prostie3 = theRepChars[j-1];
char prostie4 = theRepChars[j];
String prostiaMare = prostie4 + prostie3 + prostie2 + prostie1 + "";
System.out.println(prostiaMare);
}
}
}
}
}
And trying to create a new byte with the value that prostiaMare has gives me the numberFormatException. I'm not sure if I have not followed the algorithm right ( http://www.herongyang.com/encoding/UUEncode-Algorithm.html )
196 is outside the range of byte, a signed value. Bytes can range from -128 to 127.
I'm not sure why you're casting to String. If you just want a byte with bits equivalent those of the sum of the four chars, cast directly to byte:
(byte) (prostie4 + prostie3 + prostie2 + prostie1)
If you intended to construct a String from the four chars, you are not currently doing that. Use:
"" + prostie4 + prostie3 + prostie2 + prostie1
and, if the result is in the range of a byte, you can create a byte as you have been.
Bytes are signed in Java. Which means a byte, which is 8 bits long, has a minimum value of -2^7 (-128) and a max value of 2^7 - 1 (127). Java has no unsigned primitive types apart from char (unsigned, 16bit).
Therefore 196 is unparseable --> NumberFormatException.
You don't have much to work around this except to read into a larger type and do & 0xff to obtain the byte:
final int i = Integer.parseInt(theString);
final byte b = (byte) (i & 0xff);
Or do yourself a favour and use Guava, which has UnsignedBytes:
final byte b = UnsignedBytes.parseUnsignedByte(theString);
But it appears that you want to do comparisons anyway; so just use a larger type than byte. And no, this won't waste memory: don't forget about alignment.
As mentioned in the docs
An exception of type NumberFormatException is thrown if any of the following situations occurs:
The first argument is null or is a string of length zero.
The radix is either smaller than Character.MIN_RADIX or larger than Character.MAX_RADIX.
Any character of the string is not a digit of the specified radix, except that the first - character may be a minus sign '-' ('\u002D') provided that the string is longer than length 1.
The value represented by the string is not a value of type byte.
In your case its the last case since 196 cant be represented as byte..The valid range is -128 to 127

some thing wrong with array index in byte[]

I have string like this
String text = "f001050000000000003d61c1c1df400200c0000009181600ef014000003f20"
I converted it to bytes to loop through it as bytes
byte[] bytes = new BigInteger(text,16).toByteArray();
for (int i = 0; i < bytes.length; i++)
{
System.out.print(String.format("%02x ", bytes[i]));
}
But when I print array values it adds byte 00 at the beginning of the actual string!
It should start with f0 but it starts with 00!
When I start index with 1 this 00 disappear.
From where this 00 come!?
JavaDoc of BigInteger#toByteArray() states:
Returns a byte array containing the two's-complement
representation of this BigInteger. The byte array will be in big-endian byte-order: the most significant byte is in the zeroth element. The array will contain the minimum number of bytes required to represent this BigInteger, including at least one sign bit, which is (ceil((this.bitLength() + 1)/8)).[...]
As you have a positive number, the first bit will be zero in two's complement.
I think BigInteger is no good for this task. You need to parse your text yourself, it's not difficult
byte[] bytes = new byte[text.length() / 2];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) ((Character.digit(text.charAt(i * 2), 16) << 4) + Character.digit(text.charAt(i * 2 + 1), 16));
}
not that it also converts "0000f0..." correctly but BigInteger would truncate leading zeroes (normalize) because for BigInteger it is just a number

Java - Change int to ascii

Is there a way for java to convert int's to ascii symbols?
Do you want to convert ints to chars?:
int yourInt = 33;
char ch = (char) yourInt;
System.out.println(yourInt);
System.out.println(ch);
// Output:
// 33
// !
Or do you want to convert ints to Strings?
int yourInt = 33;
String str = String.valueOf(yourInt);
Or what is it that you mean?
If you first convert the int to a char, you will have your ascii code.
For example:
int iAsciiValue = 9; // Currently just the number 9, but we want Tab character
// Put the tab character into a string
String strAsciiTab = Character.toString((char) iAsciiValue);
There are many ways to convert an int to ASCII (depending on your needs) but here is a way to convert each integer byte to an ASCII character:
private static String toASCII(int value) {
int length = 4;
StringBuilder builder = new StringBuilder(length);
for (int i = length - 1; i >= 0; i--) {
builder.append((char) ((value >> (8 * i)) & 0xFF));
}
return builder.toString();
}
For example, the ASCII text for "TEST" can be represented as the byte array:
byte[] test = new byte[] { (byte) 0x54, (byte) 0x45, (byte) 0x53, (byte) 0x54 };
Then you could do the following:
int value = ByteBuffer.wrap(test).getInt(); // 1413829460
System.out.println(toASCII(value)); // outputs "TEST"
...so this essentially converts the 4 bytes in a 32-bit integer to 4 separate ASCII characters (one character per byte).
You can convert a number to ASCII in java. example converting a number 1 (base is 10) to ASCII.
char k = Character.forDigit(1, 10);
System.out.println("Character: " + k);
System.out.println("Character: " + ((int) k));
Output:
Character: 1
Character: 49
In fact in the last answer
String strAsciiTab = Character.toString((char) iAsciiValue);
the essential part is (char)iAsciiValue which is doing the job (Character.toString useless)
Meaning the first answer was correct actually
char ch = (char) yourInt;
if in yourint=49 (or 0x31), ch will be '1'
In Java, you really want to use Integer.toString to convert an integer to its corresponding String value. If you are dealing with just the digits 0-9, then you could use something like this:
private static final char[] DIGITS =
{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
private static char getDigit(int digitValue) {
assertInRange(digitValue, 0, 9);
return DIGITS[digitValue];
}
Or, equivalently:
private static int ASCII_ZERO = 0x30;
private static char getDigit(int digitValue) {
assertInRange(digitValue, 0, 9);
return ((char) (digitValue + ASCII_ZERO));
}
The most simple way is using type casting:
public char toChar(int c) {
return (char)c;
}
tl;dr
Use Character#toString, not char.
String result = Character.toString( yourAsciiNumber ) ;
Ex:
Character.toString( 97 ) // LATIN SMALL LETTER A
a
Character.toString( 128_567 ) // FACE WITH MEDICAL MASK
😷
char is legacy
The char type in Java is legacy, and is essentially broken. As a 16-bit value, char is incapable of representing most characters defined by Unicode.
This succeeds:
System.out.println( Character.toString( 128_567 )); // Unicode code points handle full-range of Unicode characters.
😷
This fails:
System.out.println( ( char ) 128_567 ); // `char` fails with most Unicode characters.
See code run live at IdeOne.com.
Code point
Use code point integer numbers to represent individual letters.
US-ASCII is a subset of Unicode. So, any US-ASCII number (0-127) is also a Unicode code point (0-1,114,111).
To change a code point number to a String object containing a single character, call Character#toString.
String x = Character.toString( 97 ) ;
a
See this code run live at IdeOne.com.
The most simple way is to get integer and just use the casting operator
Ex
int num = 33;
System.out.println((char) num); //Outputs 33
//if you want to find the integer value of character instead.
//Just do the reverse
char ch = '%';
System.out.println((int) ch);

Java code To convert byte to Hexadecimal

I have an array of bytes.
I want each byte String of that array to be converted to its corresponding hexadecimal values.
Is there any function in Java to convert a byte array to Hexadecimal ?
byte[] bytes = {-1, 0, 1, 2, 3 };
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02X ", b));
}
System.out.println(sb.toString());
// prints "FF 00 01 02 03 "
See also
java.util.Formatter syntax
%[flags][width]conversion
Flag '0' - The result will be zero-padded
Width 2
Conversion 'X' - The result is formatted as a hexadecimal integer, uppercase
Looking at the text of the question, it's also possible that this is what is requested:
String[] arr = {"-1", "0", "10", "20" };
for (int i = 0; i < arr.length; i++) {
arr[i] = String.format("%02x", Byte.parseByte(arr[i]));
}
System.out.println(java.util.Arrays.toString(arr));
// prints "[ff, 00, 0a, 14]"
Several answers here uses Integer.toHexString(int); this is doable, but with some caveats. Since the parameter is an int, a widening primitive conversion is performed to the byte argument, which involves sign extension.
byte b = -1;
System.out.println(Integer.toHexString(b));
// prints "ffffffff"
The 8-bit byte, which is signed in Java, is sign-extended to a 32-bit int. To effectively undo this sign extension, one can mask the byte with 0xFF.
byte b = -1;
System.out.println(Integer.toHexString(b & 0xFF));
// prints "ff"
Another issue with using toHexString is that it doesn't pad with zeroes:
byte b = 10;
System.out.println(Integer.toHexString(b & 0xFF));
// prints "a"
Both factors combined should make the String.format solution more preferrable.
References
JLS 4.2.1 Integral Types and Values
For byte, from -128 to 127, inclusive
JLS 5.1.2 Widening Primitive Conversion
I am posting because none of the existing answers explain why their approaches work, which I think is really important for this problem. In some cases, this causes the proposed solution to appear unnecessarily complicated and subtle. To illustrate I will provide a fairly straightforward approach, but I'll provide a bit more detail to help illustrate why it works.
First off, what are we trying to do? We want to convert a byte value (or an array of bytes) to a string which represents a hexadecimal value in ASCII. So step one is to find out exactly what a byte in Java is:
The byte data type is an 8-bit signed two's complement integer. It has a minimum value of -128 and a maximum value of 127 (inclusive). The byte data type can be useful for saving memory in large arrays, where the memory savings actually matters. They can also be used in place of int where their limits help to clarify your code; the fact that a variable's range is limited can serve as a form of documentation.
What does this mean? A few things: First and most importantly, it means we are working with 8-bits. So for example we can write the number 2 as 0000 0010. However, since it is two's complement, we write a negative 2 like this: 1111 1110. What is also means is that converting to hex is very straightforward. That is, you simply convert each 4 bit segment directly to hex. Note that to make sense of negative numbers in this scheme you will first need to understand two's complement. If you don't already understand two's complement, you can read an excellent explanation, here: http://www.cs.cornell.edu/~tomf/notes/cps104/twoscomp.html
Converting Two's Complement to Hex In General
Once a number is in two's complement it is dead simple to convert it to hex. In general, converting from binary to hex is very straightforward, and as you will see in the next two examples, you can go directly from two's complement to hex.
Examples
Example 1: Convert 2 to Hex.
1) First convert 2 to binary in two's complement:
2 (base 10) = 0000 0010 (base 2)
2) Now convert binary to hex:
0000 = 0x0 in hex
0010 = 0x2 in hex
therefore 2 = 0000 0010 = 0x02.
Example 2: Convert -2 (in two's complement) to Hex.
1) First convert -2 to binary in two's complement:
-2 (base 10) = 0000 0010 (direct conversion to binary)
1111 1101 (invert bits)
1111 1110 (add 1)
therefore: -2 = 1111 1110 (in two's complement)
2) Now Convert to Hex:
1111 = 0xF in hex
1110 = 0xE in hex
therefore: -2 = 1111 1110 = 0xFE.
Doing this In Java
Now that we've covered the concept, you'll find we can achieve what we want with some simple masking and shifting. The key thing to understand is that the byte you are trying to convert is already in two's complement. You don't do this conversion yourself. I think this is a major point of confusion on this issue. Take for example the follow byte array:
byte[] bytes = new byte[]{-2,2};
We just manually converted them to hex, above, but how can we do it in Java? Here's how:
Step 1: Create a StringBuffer to hold our computation.
StringBuffer buffer = new StringBuffer();
Step 2: Isolate the higher order bits, convert them to hex, and append them to the buffer
Given the binary number 1111 1110, we can isolate the higher order bits by first shifting them over by 4, and then zeroing out the rest of the number. Logically this is simple, however, the implementation details in Java (and many languages) introduce a wrinkle because of sign extension. Essentially, when you shift a byte value, Java first converts your value to an integer, and then performs sign extension. So while you would expect 1111 1110 >> 4 to be 0000 1111, in reality, in Java it is represented as the two's complement 0xFFFFFFFF!
So returning to our example:
1111 1110 >> 4 (shift right 4) = 1111 1111 1111 1111 1111 1111 1111 1111 (32 bit sign-extended number in two's complement)
We can then isolate the bits with a mask:
1111 1111 1111 1111 1111 1111 1111 1111 & 0xF = 0000 0000 0000 0000 0000 0000 0000 1111
therefore: 1111 = 0xF in hex.
In Java we can do this all in one shot:
Character.forDigit((bytes[0] >> 4) & 0xF, 16);
The forDigit function just maps the number you pass it onto the set of hexadecimal numbers 0-F.
Step 3: Next we need to isolate the lower order bits. Since the bits we want are already in the correct position, we can just mask them out:
1111 1110 & 0xF = 0000 0000 0000 0000 0000 0000 0000 1110 (recall sign extension from before)
therefore: 1110 = 0xE in hex.
Like before, in Java we can do this all in one shot:
Character.forDigit((bytes[0] & 0xF), 16);
Putting this all together we can do it as a for loop and convert the entire array:
for(int i=0; i < bytes.length; i++){
buffer.append(Character.forDigit((bytes[i] >> 4) & 0xF, 16));
buffer.append(Character.forDigit((bytes[i] & 0xF), 16));
}
Hopefully this explanation makes things clearer for those of you wondering exactly what is going on in the many examples you will find on the internet. Hopefully I didn't make any egregious errors, but suggestions and corrections are highly welcome!
The fastest way i've yet found to do this is the following:
private static final String HEXES = "0123456789ABCDEF";
static String getHex(byte[] raw) {
final StringBuilder hex = new StringBuilder(2 * raw.length);
for (final byte b : raw) {
hex.append(HEXES.charAt((b & 0xF0) >> 4)).append(HEXES.charAt((b & 0x0F)));
}
return hex.toString();
}
It's ~ 50x faster than String.format. if you want to test it:
public class MyTest{
private static final String HEXES = "0123456789ABCDEF";
#Test
public void test_get_hex() {
byte[] raw = {
(byte) 0xd0, (byte) 0x0b, (byte) 0x01, (byte) 0x2a, (byte) 0x63,
(byte) 0x78, (byte) 0x01, (byte) 0x2e, (byte) 0xe3, (byte) 0x6c,
(byte) 0xd2, (byte) 0xb0, (byte) 0x78, (byte) 0x51, (byte) 0x73,
(byte) 0x34, (byte) 0xaf, (byte) 0xbb, (byte) 0xa0, (byte) 0x9f,
(byte) 0xc3, (byte) 0xa9, (byte) 0x00, (byte) 0x1e, (byte) 0xd5,
(byte) 0x4b, (byte) 0x89, (byte) 0xa3, (byte) 0x45, (byte) 0x35,
(byte) 0xd6, (byte) 0x10,
};
int N = 77777;
long t;
{
t = System.currentTimeMillis();
for (int i = 0; i < N; i++) {
final StringBuilder hex = new StringBuilder(2 * raw.length);
for (final byte b : raw) {
hex.append(HEXES.charAt((b & 0xF0) >> 4)).append(HEXES.charAt((b & 0x0F)));
}
hex.toString();
}
System.out.println(System.currentTimeMillis() - t); // 50
}
{
t = System.currentTimeMillis();
for (int i = 0; i < N; i++) {
StringBuilder hex = new StringBuilder(2 * raw.length);
for (byte b : raw) {
hex.append(String.format("%02X", b));
}
hex.toString();
}
System.out.println(System.currentTimeMillis() - t); // 2535
}
}
}
Edit: Just found something just a lil faster and that holds on one line but is not compatible with JRE 9. Use at your own risks
import javax.xml.bind.DatatypeConverter;
DatatypeConverter.printHexBinary(raw);
Try this way:
byte bv = 10;
String hexString = Integer.toHexString(bv);
Dealing with array (if I understood you correctly):
byte[] bytes = {9, 10, 11, 15, 16};
StringBuffer result = new StringBuffer();
for (byte b : bytes) {
result.append(String.format("%02X ", b));
result.append(" "); // delimiter
}
return result.toString();
As polygenelubricants mentioned, String.format() is the right answer compare to Integer.toHexString() (since it deals with negative numbers in a right way).
A short and simple way to convert byte[] to hex string by using BigInteger:
import java.math.BigInteger;
byte[] bytes = new byte[] {(byte)255, 10, 20, 30};
String hex = new BigInteger(1, bytes).toString(16);
System.out.println(hex); // ff0a141e
How It Works?
The built-in system class java.math.BigInteger class (java.math.BigInteger) is compatible with binary and hex data:
Its has a constructor BigInteger(signum=1, byte[]) to create a big integer by byte[] (set its first parameter signum = 1 to handle correctly the negative bytes)
Use BigInteger.toString(16) to convert the big integer to hex string
To parse a hex number use new BigInteger("ffa74b", 16) - does not handle correctly the leading zero
If you may want to have the leading zero in the hex result, check its size and add the missing zero if necessary:
if (hex.length() % 2 == 1)
hex = "0" + hex;
Notes
Use new BigInteger(1, bytes), instead of new BigInteger(bytes), because Java is "broken by design" and the byte data type does not hold bytes but signed tiny integers [-128...127]. If the first byte is negative, the BigInteger assumes you pass a negative big integer. Just pass 1 as first parameter (signum=1).
The conversion back from hex to byte[] is tricky: sometimes a leading zero enters in the produced output and it should be cleaned like this:
byte[] bytes = new BigInteger("ffa74b", 16).toByteArray();
if (bytes[0] == 0) {
byte[] newBytes = new byte[bytes.length - 1];
System.arraycopy(bytes, 1, newBytes, 0, newBytes.length);
bytes = newBytes;
}
The last note is the if the byte[] has several leading zeroes, they will be lost.
The Best solution is this badass one-liner:
String hex=DatatypeConverter.printHexBinary(byte[] b);
as mentioned here
If you want a constant-width hex representation, i.e. 0A instead of A, so that you can recover the bytes unambiguously, try format():
StringBuilder result = new StringBuilder();
for (byte bb : byteArray) {
result.append(String.format("%02X", bb));
}
return result.toString();
If you are happy to use an external library, the org.apache.commons.codec.binary.Hex class has an encodeHex method which takes a byte[] and returns a char[]. This methods is MUCH faster than the format option, and encapsulates the details of the conversion. Also comes with a decodeHex method for the opposite conversion.
You can use the method from Bouncy Castle Provider library:
org.bouncycastle.util.encoders.Hex.toHexString(byteArray);
The Bouncy Castle Crypto package is a Java implementation of
cryptographic algorithms. This jar contains JCE provider and
lightweight API for the Bouncy Castle Cryptography APIs for JDK 1.5 to
JDK 1.8.
Maven dependency:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.60</version>
</dependency>
or from Apache Commons Codec:
org.apache.commons.codec.binary.Hex.encodeHexString(byteArray);
The Apache Commons Codec package contains simple encoder and decoders
for various formats such as Base64 and Hexadecimal. In addition to
these widely used encoders and decoders, the codec package also
maintains a collection of phonetic encoding utilities.
Maven dependency:
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.11</version>
</dependency>
This is the code that I've found to run the fastest so far. I ran it on 109015 byte arrays of length 32, in 23ms. I was running it on a VM so it'll probably run faster on bare metal.
public static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
public static char[] encodeHex( final byte[] data ){
final int l = data.length;
final char[] out = new char[l<<1];
for( int i=0,j=0; i<l; i++ ){
out[j++] = HEX_DIGITS[(0xF0 & data[i]) >>> 4];
out[j++] = HEX_DIGITS[0x0F & data[i]];
}
return out;
}
Then you can just do
String s = new String( encodeHex(myByteArray) );
Java 17: Introducing java.util.HexFormat
Java 17 comes with a utility to convert byte arrays and numbers to their hexadecimal counterparts. Let's say we have an MD5 digest of "Hello World" as a byte-array:
var md5 = MessageDigest.getInstance("md5");
md5.update("Hello world".getBytes(UTF_8));
var digest = md5.digest();
Now we can use the HexFormat.of().formatHex(byte[]) method to convert the given byte[] to its hexadecimal form:
jshell> HexFormat.of().formatHex(digest)
$7 ==> "3e25960a79dbc69b674cd4ec67a72c62"
The withUpperCase() method returns the uppercase version of the previous output:
jshell> HexFormat.of().withUpperCase().formatHex(digest)
$8 ==> "3E25960A79DBC69B674CD4EC67A72C62"
BigInteger n = new BigInteger(byteArray);
String hexa = n.toString(16);
Here is a simple function to convert byte to Hexadecimal
private static String convertToHex(byte[] data) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < data.length; i++) {
int halfbyte = (data[i] >>> 4) & 0x0F;
int two_halfs = 0;
do {
if ((0 <= halfbyte) && (halfbyte <= 9))
buf.append((char) ('0' + halfbyte));
else
buf.append((char) ('a' + (halfbyte - 10)));
halfbyte = data[i] & 0x0F;
} while(two_halfs++ < 1);
}
return buf.toString();
}
Others have covered the general case. But if you have a byte array of a known form, for example a MAC address, then you can:
byte[] mac = { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 };
String str = String.format("%02X:%02X:%02X:%02X:%02X:%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
There's your fast method:
private static final String[] hexes = new String[]{
"00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
"10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
"20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
"30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
"40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
"50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
"60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
"70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
"80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
"90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
"A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
"B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
"C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
"D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
"E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
"F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"
};
public static String byteToHex(byte b) {
return hexes[b&0xFF];
}
Just like some other answers, I recommend to use String.format() and BigInteger. But to interpret the byte array as big-endian binary representation instead of two's-complement binary representation (with signum and incomplete use of possible hex values range) use BigInteger(int signum, byte[] magnitude), not BigInteger(byte[] val).
For example, for a byte array of length 8 use:
String.format("%016X", new BigInteger(1,bytes))
Advantages:
leading zeros
no signum
only built-in functions
only one line of code
Disadvantage:
there might be more efficient ways to do that
Example:
byte[] bytes = new byte[8];
Random r = new Random();
System.out.println("big-endian | two's-complement");
System.out.println("-----------------|-----------------");
for (int i = 0; i < 10; i++) {
r.nextBytes(bytes);
System.out.print(String.format("%016X", new BigInteger(1,bytes)));
System.out.print(" | ");
System.out.print(String.format("%016X", new BigInteger(bytes)));
System.out.println();
}
Example output:
big-endian | two's-complement
-----------------|-----------------
3971B56BC7C80590 | 3971B56BC7C80590
64D3C133C86CCBDC | 64D3C133C86CCBDC
B232EFD5BC40FA61 | -4DCD102A43BF059F
CD350CC7DF7C9731 | -32CAF338208368CF
82CDC9ECC1BC8EED | -7D3236133E437113
F438C8C34911A7F5 | -BC7373CB6EE580B
5E99738BE6ACE798 | 5E99738BE6ACE798
A565FE5CE43AA8DD | -5A9A01A31BC55723
032EBA783D2E9A9F | 032EBA783D2E9A9F
8FDAA07263217ABA | -70255F8D9CDE8546
Creating (and destroying) a bunch of String instances is not a good way if performance is an issue.
Please ignore those verbose (duplicate) arguments checking statements (ifs). That's for (another) educational purposes.
Full maven project: http://jinahya.googlecode.com/svn/trunk/com.googlecode.jinahya/hex-codec/
Encoding...
/**
* Encodes a single nibble.
*
* #param decoded the nibble to encode.
*
* #return the encoded half octet.
*/
protected static int encodeHalf(final int decoded) {
switch (decoded) {
case 0x00:
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07:
case 0x08:
case 0x09:
return decoded + 0x30; // 0x30('0') - 0x39('9')
case 0x0A:
case 0x0B:
case 0x0C:
case 0x0D:
case 0x0E:
case 0x0F:
return decoded + 0x57; // 0x41('a') - 0x46('f')
default:
throw new IllegalArgumentException("illegal half: " + decoded);
}
}
/**
* Encodes a single octet into two nibbles.
*
* #param decoded the octet to encode.
* #param encoded the array to which each encoded nibbles are written.
* #param offset the offset in the array.
*/
protected static void encodeSingle(final int decoded, final byte[] encoded,
final int offset) {
if (encoded == null) {
throw new IllegalArgumentException("null encoded");
}
if (encoded.length < 2) {
// not required
throw new IllegalArgumentException(
"encoded.length(" + encoded.length + ") < 2");
}
if (offset < 0) {
throw new IllegalArgumentException("offset(" + offset + ") < 0");
}
if (offset >= encoded.length - 1) {
throw new IllegalArgumentException(
"offset(" + offset + ") >= encoded.length(" + encoded.length
+ ") - 1");
}
encoded[offset] = (byte) encodeHalf((decoded >> 4) & 0x0F);
encoded[offset + 1] = (byte) encodeHalf(decoded & 0x0F);
}
/**
* Decodes given sequence of octets into a sequence of nibbles.
*
* #param decoded the octets to encode
*
* #return the encoded nibbles.
*/
protected static byte[] encodeMultiple(final byte[] decoded) {
if (decoded == null) {
throw new IllegalArgumentException("null decoded");
}
final byte[] encoded = new byte[decoded.length << 1];
int offset = 0;
for (int i = 0; i < decoded.length; i++) {
encodeSingle(decoded[i], encoded, offset);
offset += 2;
}
return encoded;
}
/**
* Encodes given sequence of octets into a sequence of nibbles.
*
* #param decoded the octets to encode.
*
* #return the encoded nibbles.
*/
public byte[] encode(final byte[] decoded) {
return encodeMultiple(decoded);
}
Decoding...
/**
* Decodes a single nibble.
*
* #param encoded the nibble to decode.
*
* #return the decoded half octet.
*/
protected static int decodeHalf(final int encoded) {
switch (encoded) {
case 0x30: // '0'
case 0x31: // '1'
case 0x32: // '2'
case 0x33: // '3'
case 0x34: // '4'
case 0x35: // '5'
case 0x36: // '6'
case 0x37: // '7'
case 0x38: // '8'
case 0x39: // '9'
return encoded - 0x30;
case 0x41: // 'A'
case 0x42: // 'B'
case 0x43: // 'C'
case 0x44: // 'D'
case 0x45: // 'E'
case 0x46: // 'F'
return encoded - 0x37;
case 0x61: // 'a'
case 0x62: // 'b'
case 0x63: // 'c'
case 0x64: // 'd'
case 0x65: // 'e'
case 0x66: // 'f'
return encoded - 0x57;
default:
throw new IllegalArgumentException("illegal half: " + encoded);
}
}
/**
* Decodes two nibbles into a single octet.
*
* #param encoded the nibble array.
* #param offset the offset in the array.
*
* #return decoded octet.
*/
protected static int decodeSingle(final byte[] encoded, final int offset) {
if (encoded == null) {
throw new IllegalArgumentException("null encoded");
}
if (encoded.length < 2) {
// not required
throw new IllegalArgumentException(
"encoded.length(" + encoded.length + ") < 2");
}
if (offset < 0) {
throw new IllegalArgumentException("offset(" + offset + ") < 0");
}
if (offset >= encoded.length - 1) {
throw new IllegalArgumentException(
"offset(" + offset + ") >= encoded.length(" + encoded.length
+ ") - 1");
}
return (decodeHalf(encoded[offset]) << 4)
| decodeHalf(encoded[offset + 1]);
}
/**
* Encodes given sequence of nibbles into a sequence of octets.
*
* #param encoded the nibbles to decode.
*
* #return the encoded octets.
*/
protected static byte[] decodeMultiple(final byte[] encoded) {
if (encoded == null) {
throw new IllegalArgumentException("null encoded");
}
if ((encoded.length & 0x01) == 0x01) {
throw new IllegalArgumentException(
"encoded.length(" + encoded.length + ") is not even");
}
final byte[] decoded = new byte[encoded.length >> 1];
int offset = 0;
for (int i = 0; i < decoded.length; i++) {
decoded[i] = (byte) decodeSingle(encoded, offset);
offset += 2;
}
return decoded;
}
/**
* Decodes given sequence of nibbles into a sequence of octets.
*
* #param encoded the nibbles to decode.
*
* #return the decoded octets.
*/
public byte[] decode(final byte[] encoded) {
return decodeMultiple(encoded);
}
This is a very fast way. No external libaries needed.
final protected static char[] HEXARRAY = "0123456789abcdef".toCharArray();
public static String encodeHexString( byte[] bytes ) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEXARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEXARRAY[v & 0x0F];
}
return new String(hexChars);
}
I couldn't figure out what exactly you meant by byte String, but here are some conversions from byte to String and vice versa, of course there is a lot more on the official documentations
Integer intValue = 149;
The corresponding byte value is:
Byte byteValue = intValue.byteValue(); // this will convert the rightmost byte of the intValue to byte, because Byte is an 8 bit object and Integer is at least 16 bit, and it will give you a signed number in this case -107
get the integer value back from a Byte variable:
Integer anInt = byteValue.intValue(); // This will convert the byteValue variable to a signed Integer
From Byte and Integer to hex String:
This is the way I do it:
Integer anInt = 149
Byte aByte = anInt.byteValue();
String hexFromInt = "".format("0x%x", anInt); // This will output 0x95
String hexFromByte = "".format("0x%x", aByte); // This will output 0x95
Converting an array of bytes to a hex string:
As far as I know there is no simple function to convert all the elements inside an array of some Object to elements of another Object, So you have to do it yourself. You can use the following functions:
From byte[] to String:
public static String byteArrayToHexString(byte[] byteArray){
String hexString = "";
for(int i = 0; i < byteArray.length; i++){
String thisByte = "".format("%x", byteArray[i]);
hexString += thisByte;
}
return hexString;
}
And from hex string to byte[]:
public static byte[] hexStringToByteArray(String hexString){
byte[] bytes = new byte[hexString.length() / 2];
for(int i = 0; i < hexString.length(); i += 2){
String sub = hexString.substring(i, i + 2);
Integer intVal = Integer.parseInt(sub, 16);
bytes[i / 2] = intVal.byteValue();
String hex = "".format("0x%x", bytes[i / 2]);
}
return bytes;
}
It is too late but I hope this could help some others ;)
If you like streams, here is a single-expression version of the format-and-concatenate approach:
String hex = IntStream.range(0, bytes.length)
.map(i -> bytes[i] & 0xff)
.mapToObj(b -> String.format("%02x", b))
.collect(Collectors.joining());
It's a shame there isn't a method like Arrays::streamUnsignedBytes for this.
If you use Tink, then there is:
package com.google.crypto.tink.subtle;
public final class Hex {
public static String encode(final byte[] bytes) { ... }
public static byte[] decode(String hex) { ... }
}
so something like this should work:
import com.google.crypto.tink.subtle.Hex;
byte[] bytes = {-1, 0, 1, 2, 3 };
String enc = Hex.encode(bytes);
byte[] dec = Hex.decode(enc)
Just adding my two cents as I see many answers using a char array and/or using a StringBuilder instance and claiming to be fast or faster.
Since I had a different idea using the ASCII table organization having 0-9 at code points 48-57 and A-F at 65-70 and a-f at 97-102, I wanted to test which idea is the fastest.
Since I have not done something like that for quite some years now, I am giving this an extensive shoot out. I use 1 billion bytes in different sized arrays (1M, 1K, 10) so we have 1000 times 1M bytes per array, 1M times 1000 bytes per array and 100M times for 10 bytes per array.
Turns out the char array from 1-F wins. Using a char array as output instead of a StringBuilder also wins hands down (less objects, no test of capacity and no new array nor copying when growing). Furthermore you seem to get a small penalty when using a foreach (for(var b : bytes)) loop.
The version using my idea was approx. 15% slower for 1M bytes per array, 21% slower for 1000 bytes per array and 18% slower for 10 bytes per array. The StringBuilder version was 210%, 380% and 310% slower.
Bad but not that unexpected as a lookup of small arrays in first level cache beats one if and an add... . (one cache access + offset calculation vs. one if, one jump, one add -> not sure about the jump thou).
My version:
public static String bytesToHex(byte [] bytes) {
char [] result = new char [bytes.length * 2];
for(int index = 0; index < bytes.length; index++) {
int v = bytes[index];
int upper = (v >>> 4) & 0xF;
result[index * 2] = (char)(upper + (upper < 10 ? 48 : 65 - 10));
int lower = v & 0xF;
result[index * 2 + 1] = (char)(lower + (lower < 10 ? 48 : 65 - 10));
}
return new String(result);
}
PS: Yes I did multiple runs and took the best run each, did warm up and also did runs with 10 billion characters to be sure same picture... .
Use
Integer.toHexString((int)b);

Creating a ISO-8859-1 string from a HEX-string in Java, shifting bits

I am trying to convert a HEX-sequence to a String encoded in either, ISO-8859-1, UTF-8 or UTF-16BE. That is, I have a String looking like: "0422043504410442" this represents the characters: "Test" in UTF-16BE.
The code I used to convert between the two formats was:
private static String hex2String(String hex, String encoding) throws UnsupportedEncodingException {
char[] hexArray = hex.toCharArray();
int length = hex.length() / 2;
byte[] rawData = new byte[length];
for(int i=0; i<length; i++){
int high = Character.digit(hexArray[i*2], 16);
int low = Character.digit(hexArray[i*2+1], 16);
int value = (high << 4) | low;
if( value > 127)
value -= 256;
rawData[i] = (byte) value;
}
return new String(rawData, encoding);
}
This seems to work fine for me, but I still have two questions regarding this:
Is there any simpler way (preferably without bit-handling) to do this conversion?
How am I to interpret the line: int value = (high << 4) | low;?
I am familiar with the basics of bit-handling, though not at all with the Java syntax. I believe the first part shift all bits to the left by 4 steps. Though the rest I don't understand and why it would be helpful in this certain situation.
I apologize for any confusion in my question, please let me know if I should clarify anything.
Thank you.
//Abeansits
Is there any simpler way (preferably without bit-handling) to do this conversion?
None I would know of - the only simplification seems to parse the whole byte at once rather than parsing digit by digit (e.g. using int value = Integer.parseInt(hex.substring(i * 2, i * 2 + 2), 16);)
public static byte[] hexToBytes(final String hex) {
final byte[] bytes = new byte[hex.length() / 2];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) Integer.parseInt(hex.substring(i * 2, i * 2 + 2), 16);
}
return bytes;
}
How am I to interpret the line: int value = (high << 4) | low;?
look at this example for your last two digits (42):
int high = 4; // binary 0100
int low = 2; // binary 0010
int value = (high << 4) | low;
int value = (0100 << 4) | 0010; // shift 4 to left
int value = 01000000 | 0010; // bitwise or
int value = 01000010;
int value = 66; // 01000010 == 0x42 == 66
You can replace the << and | in this case with * and +, but I don't recommend it.
The expression
int value = (high << 4) | low;
is equivalent to
int value = high * 16 + low;
The subtraction of 256 to get a value between -128 and 127 is unnecessary. Simply casting, for example, 128 to a byte will produce the correct result. The lowest 8 bits of the int 128 have the same pattern as the byte -128: 0x80.
I'd write it simply as:
rawData[i] = (byte) ((high << 4) | low);
Is there any simpler way (preferably
without bit-handling) to do this
conversion?
You can use the Hex class in Apache commons, but internally, it will do the same thing, perhaps with minor differences.
How am I to interpret the line: int value = (high << 4) | low;?
This combines two hex digits, each of which represents 4 bits, into one unsigned 8-bit value stored as an int. The next two lines convert this to a signed Java byte.

Categories

Resources