I'm looking for a way, how to reversibly convert a byte[] of an arbitrary length to positive number (String representation in numbers).
BigInteger offers a solution:
byte[] originalBytes = ...
String string = new BigInteger(originalBytes).toString();
...
byte[] decodedBytes = new BigInteger(string).toByteArray();
However, I'm not sure how to get gracefully rid of negative values (or where to store the sign) and keep the process reversible.
Edit: just replace
String string = new BigInteger(originalBytes).toString();
with
String string = new BigInteger(1, originalBytes).toString();
The 1, signals that the passed array represents a positive number (signum = 1)
Original:
You can just prefix the array with a zero byte:
byte[] original = new byte[] { (byte) 255 };
System.out.println(new BigInteger(original).toString()); // prints "-1"
byte[] paddedCopy = new byte[original.length + 1];
for (int i = 0; i < original.length; i++) {
paddedCopy[i + 1] = original[i];
}
System.out.println(new BigInteger(paddedCopy).toString()); // prints "255"
This will essentially nullify the sign bit, making the number unsigned.
Related
i get temperature from serial port ,which gives symbols instead number .
try {
while (inputStream.available() > 0) {
int n = inputStream.available();
readBuffer = new byte[n];
inputStream.read(readBuffer, 0, n);
}
int i=1;
String y = new String(readBuffer, StandardCharsets.ISO_8859_1);
String str = new String(readBuffer, "UTF-8");
String ye = new String(readBuffer);
String b = new BigInteger(1, readBuffer).toString();
jTextField3.setText(" " + str + " ");
Wrap your inputStream in a DataInputStream, and then use the readByte(), readInt() methods as appropriate to read the correct type of data from the stream.
This is because the serial port gives you the values in binary, not text. You need to find the exact format of the values (2 or 4 bytes, signed or unsigned, ...) in order to convert them correctly to string.
Now available() is for asychrone polling, and could be 0 before yield a positive count. Best would be to read the buffer in blocking manner.
byte[] readBuffer = new byte[0];
try {
for (;;) {
int n = inputStream.available();
if (n <= 0) {
break;
}
readBuffer = new byte[n];
inputStream.read(readBuffer, 0, n);
break;
}
String y = new String(readBuffer, StandardCharsets.ISO_8859_1);
jTextField3.setText(" \"" + y + "\" ");
jTextField3.setText(Arrays.toString(readBuffer)));
}
The encoding was missing for your bytes. And best probably is to keep the data as byte array.
I have a problem with CharsetDecoder class.
First example of code (which works):
final CharsetDecoder dec = Charset.forName("UTF-8").newDecoder();
final ByteBuffer b = ByteBuffer.allocate(3);
final byte[] tab = new byte[]{(byte)-30, (byte)-126, (byte)-84}; //char €
for (int i=0; i<tab.length; i++){
b.put(tab, i, 1);
}
try {
b.flip();
System.out.println("a" + dec.decode(b).toString() + "a");
} catch (CharacterCodingException e1) {
e1.printStackTrace();
}
The result is a€a
But when i execute this code:
final CharsetDecoder dec = Charset.forName("UTF-8").newDecoder();
final CharBuffer chars = CharBuffer.allocate(3);
final byte[] tab = new byte[]{(byte)-30, (byte)-126, (byte)-84}; //char €
for (int i=0; i<tab.length; i++){
ByteBuffer buffer = ByteBuffer.wrap(tab, i, 1);
dec.decode(buffer, chars, i == 2);
}
dec.flush(chars);
System.out.println("a" + chars.toString() + "a");
The result is a
Why is not the same result?
How to use the method decode(ByteBuffer, CharBuffer, endOfInput) of class CharsetDecoder in order to retrieve the result a€a ?
-- EDIT --
So with code of Jesper I do that. It's no perfect but works with a step = 1, 2 and 3
final CharsetDecoder dec = Charset.forName("UTF-8").newDecoder();
final CharBuffer chars = CharBuffer.allocate(6);
final byte[] tab = new byte[]{(byte)97, (byte)-30, (byte)-126, (byte)-84, (byte)97, (byte)97}; //char €
final ByteBuffer buffer = ByteBuffer.allocate(10);
final int step = 3;
for (int i = 0; i < tab.length; i++) {
// Add the next byte to the buffer
buffer.put(tab, i, step);
i+=step-1;
// Remember the current position
final int pos = buffer.position();
int l=chars.position();
// Try to decode
buffer.flip();
final CoderResult result = dec.decode(buffer, chars, i >= tab.length -1);
System.out.println(result);
if (result.isUnderflow() && chars.position() == l) {
// Underflow, prepare the buffer for more writing
buffer.position(pos);
}else{
if (buffer.position() == buffer.limit()){
//ByteBuffer decoded
buffer.clear();
buffer.position(0);
}else{
//a part of ByteBuffer is decoded. We keep only bytes which are not decoded
final byte[] b = buffer.array();
final int f = buffer.position();
final int g = buffer.limit() - buffer.position();
buffer.clear();
buffer.position(0);
buffer.put(b, f, g);
}
}
buffer.limit(buffer.capacity());
}
dec.flush(chars);
chars.flip();
System.out.println(chars.toString());
The method decode(ByteBuffer, CharBuffer, boolean) returns a result, but you are ignoring the result. If print the result in your second code fragment:
for (int i = 0; i < tab.length; i++) {
ByteBuffer buffer = ByteBuffer.wrap(tab, i, 1);
System.out.println(dec.decode(buffer, chars, i == 2));
}
you'll see this output:
UNDERFLOW
MALFORMED[1]
MALFORMED[1]
a a
Apparently it does not work correctly if you start decoding in the middle of a character. The decoder expects that the first thing it reads is the start of a valid UTF-8 sequence.
edit - When the decoder reports UNDERFLOW, it expects you to add more data to the input buffer and then try to call decode() again, but you must re-offer it the data from the start of the UTF-8 sequence that you are trying to decode. You can't continue in the middle of an UTF-8 sequence.
Here is a version that works, adding one byte from tab in every iteration of the loop:
final CharsetDecoder dec = Charset.forName("UTF-8").newDecoder();
final CharBuffer chars = CharBuffer.allocate(3);
final byte[] tab = new byte[]{(byte) -30, (byte) -126, (byte) -84}; //char €
final ByteBuffer buffer = ByteBuffer.allocate(10);
for (int i = 0; i < tab.length; i++) {
// Add the next byte to the buffer
buffer.put(tab[i]);
// Remember the current position
final int pos = buffer.position();
// Try to decode
buffer.flip();
final CoderResult result = dec.decode(buffer, chars, i == 2);
System.out.println(result);
if (result.isUnderflow()) {
// Underflow, prepare the buffer for more writing
buffer.limit(buffer.capacity());
buffer.position(pos);
}
}
dec.flush(chars);
chars.flip();
System.out.println("a" + chars.toString() + "a");
The decoder does not internally cache the data from partial characters, but this does not mean that you have to do complicated things to figure out what data to re-feed the decoder. You gave it a clear way to represent what data it actually consumed, i.e. the input ByteBuffer and its position. In the second example, by giving it a new ByteBuffer every time, the OP failed to pass the decoder back what it reported it had not yet consumed.
The standard pattern for using NIO Buffers is input, flip, output, compact, loop. Short of optimization (which may be premature), there is no reason to re-implement compact yourself. You might just get it wrong, like #Jesper and #lecogiteur did (if more than a single character was ever presented). You should NOT be resetting to the position from before the decode call.
The second example should have read something like:
final CharsetDecoder dec = Charset.forName("UTF-8").newDecoder();
final CharBuffer chars = CharBuffer.allocate(3);
final byte[] tab = new byte[]{(byte)-30, (byte)-126, (byte)-84}; //char €
final ByteBuffer buffer = ByteBuffer.wrap(new byte[3]);
for (int i=0; i<tab.length; i++){
b.put(tab, i, 1); // In actual usage some type of IO read/transfer would occur here
b.flip();
dec.decode(buffer, chars, i == 2);
b.compact();
}
dec.flush(chars);
System.out.println("a" + chars.toString() + "a");
NOTE: The above does not check the return value to detect malformed input or other error handling for running safely on arbitrary input/IO conditions.
I'm trying to convert a 32-byte string of hex characters (b8aa30d8f1d398883f0eeb5079777c42) into a 16-byte string of hex values (x'b8aa30d8f1d398883f0eeb5079777c42')
I've tried some of the suggestions I've found here.
StringBuffer sb = new StringBuffer();
for (int i = 0; i < input.length(); i++) {
String s = input.substring(i, i + 2);
byte value = Byte.valueOf(s, 16);
sb.append(value);
}
this resulted in this Exception: Value out of range. Value:"b8" Radix:16
String bin = "";
String binFragment = "";
int iHex;
hex = hex.trim();
hex = hex.replaceFirst("0x", "");
for(int i = 0; i < hex.length(); i++){
iHex = Integer.parseInt(""+hex.charAt(i),16);
binFragment = Integer.toBinaryString(iHex);
while(binFragment.length() < 4){
binFragment = "0" + binFragment;
}
bin += binFragment;
}
this doesn't handle leading 0's correctly
does anyone have a way to do this conversion?
I also tried this:
public static void convert() {
String s = "0c871eea3af7526f9b2e7054f6277984";
byte[] bytes = DatatypeConverter.parseHexBinary(s);
StringBuilder outputStringBuilder = new StringBuilder();
for (byte currentByte : bytes) {
outputStringBuilder.append((char) currentByte);
}
System.out.println(String.format("%s, %s", bytes, bytes.length));
String out = bytes.toString();
System.out.println(String.format("%s, %s", out, out.length()));
}
and got these results:
[B#f01a1e, 16
[B#f01a1e, 9
There are 16 bytes in the byte array before creating the output string, but only 9 after.
If I understand you correctly, you are trying to convert a sequence of bytes (given in hexadecimal notation as a String) to the corresponding characters.
You can use the DatatypeConverter class to convert the hex-string into an array of bytes ...
byte[] bytes = DatatypeConverter.parseHexBinary(inputString);
... and convert each byte to the character it represents:
StringBuilder outputStringBuilder = new StringBuilder();
for (byte currentByte : bytes) {
outputStringBuilder.append((char) currentByte);
}
Well, the problem with your second attempt is that you read two hex characters at a time (which is correct, since each pair of hex characters is a single byte), but your loop condition and increment are wrong.
Try this:
StringBuilder sb = new StringBuilder ();
for (int i = 0; i < input.length()/2; i+=2) {
String s = input.substring(i, i + 2);
byte value = Byte.valueOf(s, 16);
sb.append(value);
}
In addition you should also check that the input has an even number of hex characters.
as the title says, how do I do it? Its easy to convert from string -> byte -> string binary, But how do I convert back? Below is a example.
The output is :
'f' to binary: 01100110
294984
I read somewhere that I could use the Integer.parseInt but clearly that is not the case :( Or am I doing something wrong?
Thanks,
:)
public class main{
public static void main(String[] args) {
String s = "f";
byte[] bytes = s.getBytes();
StringBuilder binary = new StringBuilder();
for (byte b : bytes)
{
int val = b;
for (int i = 0; i < 8; i++)
{
binary.append((val & 128) == 0 ? 0 : 1);
val <<= 1;
}
binary.append(' ');
}
System.out.println("'" + s + "' to binary: " + binary);
System.out.println(Integer.parseInt("01100110", 2));
}
}
You can use Byte.parseByte() with a radix of 2:
byte b = Byte.parseByte(str, 2);
Using your example:
System.out.println(Byte.parseByte("01100110", 2));
102
You can parse it to an integer in base 2, and convert to a byte array.
In your example you've got 16 bits you can also use short.
short a = Short.parseShort(b, 2);
ByteBuffer bytes = ByteBuffer.allocate(2).putShort(a);
byte[] array = bytes.array();
Just in case if you need it for a Very Big String.
String b = "0110100001101001";
byte[] bval = new BigInteger(b, 2).toByteArray();
I made like this, converted a string s -> byte[] and then used Integer.toBinaryString to get binaryStringRep. I converted bianryStringRep by using Byte.parseByte to get the bianryStringRep into byte and the String(newByte[]) to get the byte[] into a String! Hope it helps others then me aswell! ^^
public class main{
public static void main(String[] args) throws UnsupportedEncodingException {
String s = "foo";
byte[] bytes = s.getBytes();
byte[] newBytes = new byte[s.getBytes().length];
for(int i = 0; i < bytes.length; i++){
String binaryStringRep = String.format("%8s", Integer.toBinaryString(bytes[i] & 0xFF)).replace(' ', '0');
byte newByte = Byte.parseByte(binaryStringRep, 2);
newBytes[i] = newByte;
}
String str = new String(newBytes, "UTF-8");
System.out.println(str);
}
}
I have many hexcodes here and I want to get them into Java without appending 0x to every entity. Like:
0102FFAB and I have to do the following:
byte[] test = {0x01, 0x02, 0xFF, 0xAB};
And I have many hexcodes which are pretty long. Is there any way to make this automatically?
You could try and put the hex codes into a string and then iterate over the string, similar to this:
String input = "0102FFAB";
byte[] bytes = new byte[input.length() / 2];
for( int i = 0; i < input.length(); i+=2)
{
bytes[i/2] = Integer.decode( "0x" + input.substring( i, i + 2 ) ).byteValue();
}
Note that this requires even length strings and it is quite a quick and dirty solution. However, it should still get you started.
You can use BigInteger to load a long hex string.
public static void main(String[] args) {
String hex = "c33b2cfca154c3a3362acfbde34782af31afb606f6806313cc0df40928662edd3ef1d630ab1b75639154d71ed490a36e5f51f6c9d270c4062e8266ad1608bdc496a70f6696fa6e7cd7078c6674188e8a49ecba71fad049a3d483ccac45d27aedfbb31d82adb8135238b858143492b1cbda2e854e735909256365a270095fc";
byte[] bytes2 = hexToBytes(hex);
for(byte b: bytes2)
System.out.printf("%02x", b & 0xFF);
}
public static byte[] hexToBytes(String hex) {
// add a 10 to the start to avoid sign issues, or an odd number of characters.
BigInteger bi2 = new BigInteger("10" +hex, 16);
byte[] bytes2 = bi2.toByteArray();
byte[] bytes = new byte[bytes2.length-1];
System.arraycopy(bytes2, 1, bytes, 0, bytes.length);
return bytes;
}
prints
0c33b2cfca154c3a3362acfbde34782af31afb606f6806313cc0df40928662edd3ef1d630ab1b75639154d71ed490a36e5f51f6c9d270c4062e8266ad1608bdc496a70f6696fa6e7cd7078c6674188e8a49ecba71fad049a3d483ccac45d27aedfbb31d82adb8135238b858143492b1cbda2e854e735909256365a270095fc
note: it handles the possibility that there is one hex value short at the start.