array - java lang illegalargumentexception hexbinary needs to be even length
Código Java Para convertir bytes a Hexadecimal (16)
Tengo una matriz de bytes. Quiero que cada cadena de bytes de esa matriz se convierta a sus valores hexadecimales correspondientes.
¿Hay alguna función en Java para convertir una matriz de bytes a Hexadecimal?
Aquí hay una función simple para convertir el byte a 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();
}
Crear (y destruir) un montón de instancias String
no es una buena manera si el rendimiento es un problema.
Por favor, ignore esos enunciados de verificación de argumentos detallados (duplicados) ( if
es). Eso es para (otro) propósito educativo.
Proyecto completo de maven: http://jinahya.googlecode.com/svn/trunk/com.googlecode.jinahya/hex-codec/
Codificando ...
/**
* 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);
}
Descodificación...
/**
* 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);
}
Esta es una forma muy rápida. No se necesitan libraries externos.
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);
}
Este es el código que he encontrado para ejecutar el más rápido hasta ahora. Lo ejecuté en matrices de 109015 bytes de longitud 32, en 23 ms. Lo estaba ejecutando en una VM así que probablemente funcionará más rápido en metal desnudo.
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;
}
Entonces puedes hacer
String s = new String( encodeHex(myByteArray) );
Estoy publicando porque ninguna de las respuestas existentes explica por qué funcionan sus enfoques, que creo que es realmente importante para este problema. En algunos casos, esto hace que la solución propuesta parezca innecesariamente complicada y sutil. Para ilustrar, proporcionaré un enfoque bastante directo, pero proporcionaré un poco más de detalle para ayudar a ilustrar por qué funciona.
En primer lugar, ¿qué estamos tratando de hacer? Queremos convertir un valor de byte (o una matriz de bytes) a una cadena que representa un valor hexadecimal en ASCII. El primer paso es averiguar exactamente qué es un byte en Java:
El tipo de datos de bytes es un entero de complemento de dos firmado de 8 bits . Tiene un valor mínimo de -128 y un valor máximo de 127 (inclusive). El tipo de datos de bytes puede ser útil para guardar la memoria en matrices grandes, donde el ahorro de memoria realmente importa. También se pueden usar en lugar de int, donde sus límites ayudan a aclarar su código; el hecho de que el rango de una variable sea limitado puede servir como una forma de documentación.
¿Qué significa esto? Algunas cosas: Primero y más importante, significa que estamos trabajando con 8 bits . Entonces, por ejemplo, podemos escribir el número 2 como 0000 0010. Sin embargo, dado que es un complemento de dos, escribimos un 2 negativo así: 1111 1110. Lo que también significa es que convertir a hexadecimal es muy sencillo. Es decir, simplemente convierte cada segmento de 4 bits directamente en hexadecimal. Tenga en cuenta que para dar sentido a los números negativos en este esquema, primero deberá comprender el complemento de dos. Si aún no entiende el complemento de dos, puede leer una excelente explicación, aquí: http://www.cs.cornell.edu/~tomf/notes/cps104/twoscomp.html
Convirtiendo Complemento Dos a Maleficio en General
Una vez que un número está en el complemento de dos, es muy simple convertirlo en hex. En general, convertir de binario a hexadecimal es muy sencillo, y como verá en los dos ejemplos siguientes, puede ir directamente del complemento de dos al hex.
Ejemplos
Ejemplo 1: convierte 2 a hexadecimal
1) Primero convierte 2 a binario en complemento a dos:
2 (base 10) = 0000 0010 (base 2)
2) Ahora convierta binario a hexadecimal:
0000 = 0x0 in hex
0010 = 0x2 in hex
therefore 2 = 0000 0010 = 0x02.
Ejemplo 2: convierte -2 (en complemento de dos) a Hex.
1) Primero convierta -2 a binario en complemento a dos:
-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) Ahora convierte a maleficio:
1111 = 0xF in hex
1110 = 0xE in hex
therefore: -2 = 1111 1110 = 0xFE.
Haciendo esto en Java
Ahora que hemos cubierto el concepto, descubrirá que podemos lograr lo que queremos con un simple enmascaramiento y cambio. La clave para entender es que el byte que está tratando de convertir ya está en el complemento de dos. Usted no hace esta conversión usted mismo. Creo que este es un gran punto de confusión sobre este tema. Tomemos como ejemplo la matriz de bytes siguiente:
byte[] bytes = new byte[]{-2,2};
Simplemente los convertimos manualmente a hexadecimal, arriba, pero ¿cómo podemos hacerlo en Java? Así es cómo:
Paso 1: Crea un StringBuffer para mantener nuestro cálculo.
StringBuffer buffer = new StringBuffer();
Paso 2: aísle los bits de orden superior, conviértalos a hexadecimal y añádalos al búfer
Dado el número binario 1111 1110, podemos aislar los bits de orden superior desplazándolos primero por 4, y luego poniendo a cero el resto del número. Lógicamente, esto es simple, sin embargo, los detalles de implementación en Java (y en muchos idiomas) introducen una arruga debido a la extensión de los signos. Esencialmente, cuando cambia un valor de byte, Java primero convierte su valor en un entero, y luego realiza una extensión de signo. Entonces, mientras espera que 1111 1110 >> 4 sea 0000 1111, en realidad, en Java se representa como el complemento de dos 0xFFFFFFFF!
Volviendo a nuestro ejemplo:
1111 1110 >> 4 (shift right 4) = 1111 1111 1111 1111 1111 1111 1111 1111 (32 bit sign-extended number in two''s complement)
Entonces podemos aislar los bits con una máscara:
1111 1111 1111 1111 1111 1111 1111 1111 & 0xF = 0000 0000 0000 0000 0000 0000 0000 1111
therefore: 1111 = 0xF in hex.
En Java podemos hacer esto de una sola vez:
Character.forDigit((bytes[0] >> 4) & 0xF, 16);
La función forDigit simplemente asigna el número que pasa al conjunto de números hexadecimales 0-F.
Paso 3: A continuación, necesitamos aislar los bits de orden inferior. Como los bits que queremos ya están en la posición correcta, podemos enmascararlos:
1111 1110 & 0xF = 0000 0000 0000 0000 0000 0000 0000 1110 (recall sign extension from before)
therefore: 1110 = 0xE in hex.
Al igual que antes, en Java podemos hacer todo esto de una sola vez:
Character.forDigit((bytes[0] & 0xF), 16);
Si unimos todo esto, podemos hacerlo como un bucle for y convertir toda la matriz:
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));
}
Esperemos que esta explicación aclare las cosas para aquellos de ustedes que se preguntan exactamente qué está pasando en los muchos ejemplos que encontrarán en Internet. Espero no haber cometido ningún error atroz, ¡pero las sugerencias y correcciones son muy bienvenidas!
La forma más rápida que he encontrado para hacer esto es la siguiente:
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();
}
Es ~ 50x más rápido que String.format
. si quieres probarlo:
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
}
}
}
Editar : Acabo de encontrar algo solo un poco más rápido y que se mantiene en una línea pero no es compatible con JRE 9. Úselo bajo sus propios riesgos
import javax.xml.bind.DatatypeConverter;
DatatypeConverter.printHexBinary(raw);
La mejor solución es este one-liner rudo:
String hex=DatatypeConverter.printHexBinary(byte[] b);
como se menciona here
No pude entender a qué se refería exactamente byte String, pero aquí hay algunas conversiones de byte a String y viceversa, por supuesto que hay mucho más en la documentación oficial.
Integer intValue = 149;
El valor de byte correspondiente es:
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
recuperar el valor entero de una variable Byte:
Integer anInt = byteValue.intValue(); // This will convert the byteValue variable to a signed Integer
De Byte e Integer a hex Cadena:
Esta es la forma en que lo hago:
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
Convertir una matriz de bytes a una cadena hexagonal:
Por lo que yo sé, no hay una función simple para convertir todos los elementos dentro de una matriz de algún Object
en elementos de otro Object
, por lo que tienes que hacerlo tú mismo. Puede usar las siguientes funciones:
De byte [] a 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;
}
Y de cadena hexadecimal a 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;
}
Es demasiado tarde, pero espero que esto pueda ayudar a otros;)
Otros han cubierto el caso general. Pero si tiene una matriz de bytes de una forma conocida, por ejemplo, una dirección MAC, puede:
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]);
Pruebe de esta manera:
byte bv = 10;
String hexString = Integer.toHexString(bv);
Tratar con matriz (si te entendí correctamente):
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();
Como String.format()
mencionado, String.format()
es la respuesta correcta en comparación con Integer.toHexString()
(ya que trata los números negativos de una manera correcta).
Si desea una representación hexadecimal de ancho constante, es decir, 0A
lugar de A
, para que pueda recuperar los bytes de forma inequívoca, intente format()
:
StringBuilder result = new StringBuilder();
for (byte bb : byteArray) {
result.append(String.format("%02X", bb));
}
return result.toString();
Si está contento de utilizar una biblioteca externa, la clase org.apache.commons.codec.binary.Hex
tiene un método encodeHex
que toma un byte[]
y devuelve un char[]
. Este método es MUCHO más rápido que la opción de formato y encapsula los detalles de la conversión. También viene con un método decodeHex
para la conversión opuesta.
Utilizar
Integer.toHexString((int)b);
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 "
Ver también
- sintaxis de
java.util.Formatter
-
%[flags][width]conversion
- Bandera
''0''
- El resultado será cero-acolchado - Ancho
2
- Conversión
''X''
- El resultado se formatea como un entero hexadecimal, mayúscula
- Bandera
-
Al mirar el texto de la pregunta, también es posible que esto sea lo que se solicita:
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]"
Varias respuestas aquí usan Integer.toHexString(int)
; esto es factible, pero con algunas advertencias. Como el parámetro es un int
, se realiza una conversión primitiva de ensanchamiento al argumento byte
, que implica la extensión del signo.
byte b = -1;
System.out.println(Integer.toHexString(b));
// prints "ffffffff"
El byte
8 bits, que está firmado en Java, se extiende con signo a una int
32 bits. Para deshacer efectivamente esta extensión de signo, se puede enmascarar el byte
con 0xFF
.
byte b = -1;
System.out.println(Integer.toHexString(b & 0xFF));
// prints "ff"
Otro problema con el uso de toHexString
es que no rellena con ceros:
byte b = 10;
System.out.println(Integer.toHexString(b & 0xFF));
// prints "a"
Ambos factores combinados deberían hacer que la solución String.format
más preferible.
Referencias
- JLS 4.2.1 Tipos y valores integrales
- Por
byte
, de-128
a127
, inclusive
- Por
- JLS 5.1.2 Ampliación de la conversión primitiva
BigInteger n = new BigInteger(byteArray);
String hexa = n.toString(16));
org.bouncycastle.util.encoders.Hex.toHexString(byteArray);
o
org.apache.commons.codec.binary.Hex.encodeHexString(byteArray);