Java's BigInteger C# in Base 32 - java

How would one convert the following line of Java to C#. It generates a random BigInteger of 130 bits in size, converts it to a string in base 32 (i.e. not decimal) and then manipulates the string:
new BigInteger(130, new SecureRandom()).toString(32).replace("/", "w").toUpperCase(Locale.US);
How can I achieve that in C#?
Generate a random 130 bit BigInteger
convert it to a string in base 32
As far as the random BigInteger I have this function:
static BigInteger RandomInteger(int bits)
{
RNGCryptoServiceProvider secureRandom = new RNGCryptoServiceProvider();
// make sure there is extra room for a 0-byte so our number isn't negative
// in the case that the msb is set
var bytes = new byte[bits / 8 + 1];
secureRandom.GetBytes(bytes);
// mask off excess bits
bytes[bytes.Length - 1] &= (byte)((1 << (bits % 8)) - 1);
return new BigInteger(bytes);
}
taken from this question which does not address the base 32 conversion: Equivalent of Java's BigInteger in C#
However I'm not sure if that function is correct as well.
The C# code I have so far, RandomInteger being the function described above:
RandomInteger(130).ToString().Replace("/","w").ToUpper(CultureInfo.GetCultureInfo("en-US"));

The above code has quite a few bugs, if bits are whole the last number gets masked out entirely and there's a chance that the number gets positive because the new BigInteger(byte[]) overload expects a little endian signed number, so you have to prepend it with a 0 byte
static BigInteger RandomInteger(int bits)
{
var bytes = new byte[(bits + 7) / 8 + 1];
using (var rng = new RNGCryptoServiceProvider())
rng.GetBytes(bytes, 0, bytes.Length - 1);
var remainingBits = bits % 8;
if (remainingBits > 0)
bytes[bytes.Length - 2] &= (byte)((1 << remainingBits) - 1);
return new BigInteger(bytes);
}
This would work I suppose

Base 32 string
This is how I would convert to base 32. Note that I can not test this here and that my C# is a little rusty, but I think the following should be good enough to get you going (if someone sees a syntax error, please edit it out):
static string BigIntegerToString32(BigInteger bi)
{
// Obvious shortcut -- avoids problems later on.
if (bi == BigInteger.Zero)
return("0");
readonly char[] digits = new char[] {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V'
};
// Only work with positive BigInteger.
BigInteger value = BigInteger.Abs(bi);
// Collects one digit on each iteration.
StringBuilder result = new StringBuilder();
// This value is needed more often -- only converted once.
BigInteger thirtyOne = 0x1F;
while (value > BigInteger.Zero)
{
// get next digit: value % 32
result.Insert(0, digits[(int)(value & thirtyOne)]);
// shift out last digit: value = value / 32
value >>= 5;
}
// prepend '-' if negative
if (bi < BigInteger.Zero)
result.Insert(0, '-');
return result.ToString();
}
Note that for huge BigIntegers, it might make sense to use a faster, but more complicated algorithm (as I do in my Delphi BigInteger implementation), although this here is probably more or less how C#'s BigInteger (which does not use more sophisticated routines for large BigIntegers, AFAIK, unlike Java) does this too, for base 10.
Random 130 bit BigInteger
The answer by #hl3mukkel does a much better job of generating an n bit random BigInteger than the code you found and posted, so use his code to generate such a BigInteger.

Related

How to use BigInteger as a index of array [duplicate]

This question already has answers here:
What causes a java.lang.ArrayIndexOutOfBoundsException and how do I prevent it?
(26 answers)
BigDecimal.intValue() returns Unexpected Number
(5 answers)
Closed 5 days ago.
This post was edited and submitted for review 5 days ago and failed to reopen the post:
Duplicate This question has been answered, is not unique, and doesn’t differentiate itself from another question.
i am trying to convert a number with more than 10 digits in base 2 to 10 but when it reaches 11 i get an error. Please help me to fix it. Here is my code
public static final char[] hexDigits = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
public static String convertDecimalToBinary(String decimal) {
String binary = "";
BigInteger deci = new BigInteger(decimal);
BigInteger base = new BigInteger("2");
if(deci.equals(0)) {
return deci.toString();
}
while (deci.intValue() != 0) {
binary = hexDigits[deci.intValue() % 2] + binary;
deci = deci.divide(base);
}
return binary;
}

hard time understanding this code for a numerology program

private int sumCharValues (String input) {
String total = input.toLowerCase();
int result = 0;
for (int i = 0, n = total.length(); i < n; i++) {
char c = total.charAt(i);
result += (c - 'a' + 1);
}
return result;
}
I'm trying to understand this code. What does the result += (c - 'a' + 1) means?
Any help would be very much appreciated.
It gives the letter's numerical position in the alphabet.
String total = input.toLowerCase(); means all the letters are lower case. Then, c - 'a' subtracts the ascii value for 'a' from the ascii value of c. That means you'll get anything from 0-25. Adding one shifts your starting point, giving you 1-26.
Try it in your head or add a print statement in your code for this expression.
When the character in the string is 'a', the expression reads 'a' - 'a' + 1 so you can see that the result will be 1.
When you're performing arithmetic on characters, you're actually doing it on their Unicode value,
For non-accented alphabetic characters, that is the same as ASCII value.
For 'a', this value is 97, for 'b', it's 98, etc.
So the expression above returns the index of the character in the alphabet, starting with 1 for 'a'.
The a += b operator is (more or less—see below) a shortcut for a = a + (b). The expression (c - 'a' + 1) converts the character stored in c to an integer in such a way that 'a' will have the value 1, 'b' will have the value 2, etc., based on the Unicode code points of the characters in input. When the loop exits, result will be the sum of all the numerical equivalents of the characters in the input.
The compound assignment a += b isn't exactly a shortcut for a = a + (b) in a couple of ways. First, the left side is evaluated only once, so something like vec[i++] += 3 will increment i only once. Second, there is an implicit cast (if necessary) to the type of a. Thus,
byte a = 0;
a += 1; // works
a = a + 1; // compiler error -- a + 1 is an int value
The statement result += (c - 'a' + 1) is evaluated as,
First char c is converted to its ASCII value, then we get the diff between ascii value of char c and char 'a' and then we add 1 to it.
And this statement result += (c - 'a' + 1) can be rewritten as result = result + (c - 'a' + 1), i.e. we are just adding the previous value of result and assigning it again.
PS: The ASCII value of 'a' is 97 and 'z' is 122.
For e.g. if input String is "stackoverflow", it will be evaluated as,
char c = 's', result = 19
char c = 't', result = 39
char c = 'a', result = 40
char c = 'c', result = 43
char c = 'k', result = 54
char c = 'o', result = 69
char c = 'v', result = 91
char c = 'e', result = 96
char c = 'r', result = 114
char c = 'f', result = 120
char c = 'l', result = 132
char c = 'o', result = 147
char c = 'w', result = 170

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);

replace does not replace digits

I want to replace in a string every '0' with a 'F', every '1' with a 'E' and so on.
e.g. "234567890ABCDEF" should result in "DCBA9876543210"
final char[] items = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
for (int i = 0; i < 16; i++) {
newString = oldString.replace(items[i], items[15-i]);
}
unfortunately, this piece of code does not work. It replaces all Letters but not the digits. Any suggestions, why? I'm really at a loss...
Your problem is that you replace the digits to letters for i=0 to 7 and back for i=8 to 15.
If you add debug to your code and look at the iterations you'll notice how you overwrite the results of the first iterations with the replace()es of the last iterations:
234567890ABCDEF
23456789FABCDEF
23456789FABCDEF
D3456789FABCDEF
DC456789FABCDEF
DCB56789FABCDEF
DCBA6789FABCDEF
DCBA9789FABCDEF
DCBA9889FABCDEF
DCBA9779FABCDEF
DCBA6776FABCDEF
DCB56776F5BCDEF
DC456776F54CDEF
D3456776F543DEF
23456776F5432EF
23456776F54321F
234567760543210
This is because you invert the result done during the first eight replacements in your second replacements! This meant,
0-7 are converted back to 0-7, but 8 and 9 will be converted to their conterparts!

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);

Categories

Resources