Conversion du tableau de char en tableau d'octets et retour à nouveau
je cherche à convertir Un tableau Java char en un tableau byte sans créer un intermédiaire String
, car le tableau char contient un mot de passe. J'ai cherché quelques méthodes, mais elles semblent toutes échouer:
char[] password = "password".toCharArray();
byte[] passwordBytes1 = new byte[password.length*2];
ByteBuffer.wrap(passwordBytes1).asCharBuffer().put(password);
byte[] passwordBytes2 = new byte[password.length*2];
for(int i=0; i<password.length; i++) {
passwordBytes2[2*i] = (byte) ((password[i]&0xFF00)>>8);
passwordBytes2[2*i+1] = (byte) (password[i]&0x00FF);
}
String passwordAsString = new String(password);
String passwordBytes1AsString = new String(passwordBytes1);
String passwordBytes2AsString = new String(passwordBytes2);
System.out.println(passwordAsString);
System.out.println(passwordBytes1AsString);
System.out.println(passwordBytes2AsString);
assertTrue(passwordAsString.equals(passwordBytes1) || passwordAsString.equals(passwordBytes2));
l'assertion échoue toujours (et, de façon critique, lorsque le code est utilisé dans la production, le mot de passe est rejeté), mais les instructions d'impression impriment le mot de passe trois fois. Pourquoi sont -passwordBytes1AsString
et passwordBytes2AsString
différent passwordAsString
, mais semblent identiques? Est-ce que je Rate un terminator nul ou quelque chose comme ça? Que puis-je faire pour que la conversion et la non-conversion fonctionnent?
8 réponses
le problème est votre utilisation du String(byte[])
constructor, qui utilise l'encodage par défaut de la plate-forme. C'est presque jamais ce que vous devriez faire - si vous réussissez en "UTF-16" comme l'encodage du caractère pour fonctionner, vos tests seront probablement réussis. Actuellement je soupçonne que passwordBytes1AsString
et passwordBytes2AsString
ont chacun 16 caractères de long, chaque autre caractère étant U+0000.
la Conversion entre char et byte est l'encodage et le décodage d'un jeu de caractères.Je préfère que ce soit aussi clair que possible dans le code. Il ne signifie pas vraiment le volume de code supplémentaire:
Charset latin1Charset = Charset.forName("ISO-8859-1");
charBuffer = latin1Charset.decode(ByteBuffer.wrap(byteArray)); // also decode to String
byteBuffer = latin1Charset.encode(charBuffer); // also decode from String
à part:
java.nio classes et java.io Lecteur/enregistreur de classes utilisent ByteBuffer & CharBuffer (qui utilisent byte[] et char[] que la sauvegarde de tableaux). Donc souvent préférable si vous utilisez ces classes directement. Cependant, vous pouvez toujours faire:
byteArray = ByteBuffer.array(); byteBuffer = ByteBuffer.wrap(byteArray);
byteBuffer.get(byteArray); charBuffer.put(charArray);
charArray = CharBuffer.array(); charBuffer = ByteBuffer.wrap(charArray);
charBuffer.get(charArray); charBuffer.put(charArray);
Réponse Originale
public byte[] charsToBytes(char[] chars){
Charset charset = Charset.forName("UTF-8");
ByteBuffer byteBuffer = charset.encode(CharBuffer.wrap(chars));
return Arrays.copyOf(byteBuffer.array(), byteBuffer.limit());
}
public char[] bytesToChars(byte[] bytes){
Charset charset = Charset.forName("UTF-8");
CharBuffer charBuffer = charset.decode(ByteBuffer.wrap(bytes));
return Arrays.copyOf(charBuffer.array(), charBuffer.limit());
}
édité pour utiliser les charsets Standard
public byte[] charsToBytes(char[] chars)
{
final ByteBuffer byteBuffer = StandardCharsets.UTF_8.encode(CharBuffer.wrap(chars));
return Arrays.copyOf(byteBuffer.array(), byteBuffer.limit());
}
public char[] bytesToChars(byte[] bytes)
{
final CharBuffer charBuffer = StandardCharsets.UTF_8.decode(ByteBuffer.wrap(bytes));
return Arrays.copyOf(charBuffer.array(), charBuffer.limit());
}
Voici un JavaDoc page for StandardCharsets. Notez ceci sur la page JavaDoc:
ces charsets sont garantis pour être disponibles sur chaque implémentation de la plate-forme Java.
Si vous souhaitez utiliser un ByteBuffer et CharBuffer, ne pas faire de la simple .asCharBuffer()
, qui fait simplement un UTF-16 (LE ou BE, selon votre système - vous pouvez définir l'ordre des octets avec le order
méthode) conversion (depuis les chaînes Java et donc votre char[]
utilise en interne ce codage).
Utiliser Charset.forName(charsetName)
, et encode
ou decode
, ou newEncoder
/newDecoder
.
lors de la conversion de votre octet[] en chaîne, vous devez également indiquer le codage (et il doit être le même).
je voudrais faire est d'utiliser une boucle pour convertir des octets et un autre pour conver retour à char.
char[] chars = "password".toCharArray();
byte[] bytes = new byte[chars.length*2];
for(int i=0;i<chars.length;i++) {
bytes[i*2] = (byte) (chars[i] >> 8);
bytes[i*2+1] = (byte) chars[i];
}
char[] chars2 = new char[bytes.length/2];
for(int i=0;i<chars2.length;i++)
chars2[i] = (char) ((bytes[i*2] << 8) + (bytes[i*2+1] & 0xFF));
String password = new String(chars2);
vous devriez utiliser getBytes()
au lieu de toCharArray()
Remplacer la ligne
char[] password = "password".toCharArray();
byte[] password = "password".getBytes();
ceci est une extension de la réponse de Peter Lawrey. Afin d'en arrière (octets de caractères) de conversion de fonctionner correctement pour l'ensemble de la gamme de caractères, le code doit être comme suit:
char[] chars = new char[bytes.length/2];
for (int i = 0; i < chars.length; i++) {
chars[i] = (char) (((bytes[i*2] & 0xff) << 8) + (bytes[i*2+1] & 0xff));
}
nous devons "désigner" les octets avant d'utiliser (& 0xff
). Sinon, la moitié des valeurs de char possibles ne seront pas récupérées correctement. Par exemple, les caractères à l'intérieur de [0x80..0xff]
gamme seront touchés.
lorsque vous utilisez GetBytes à partir d'une chaîne en Java, le résultat de retour dépendra de l'encodage par défaut de votre ordinateur.(par exemple: StandardCharsetsUTF-8 ou StandardCharsets.ISO_8859_1etc...).
ainsi, chaque fois que vous voulez getBytes à partir d'un objet String. Assurez-vous de donner un encoder . comme :
String sample = "abc";
Byte[] a_byte = sample .getBytes(StandardCharsets.UTF_8);
Laissez-vérifier ce qui s'est passé avec le code. En java , la chaîne nommée sample, est stockée par Unicode. chaque caractère dans la chaîne stockée par 2 octet.
sample : value: "abc" in Memory(Hex): 00 61 00 62 00 63
a -> 00 61
b -> 00 62
c -> 00 63
Mais, Lorsque nous getBytes à Partir d'une Chaîne, nous avons
Byte[] a_byte = sample .getBytes(StandardCharsets.UTF_8)
//result is : 61 62 63
//length: 3 bytes
Byte[] a_byte = sample .getBytes(StandardCharsets.UTF_16BE)
//result is : 00 61 00 62 00 63
//length: 6 bytes
afin d'obtenir le byte oringle de la chaîne. Nous pouvons juste lire la mémoire de la corde et obtenir chaque octet de la corde.Ci-dessous est un exemple de Code:
public static byte[] charArray2ByteArray(char[] chars){
int length = chars.length;
byte[] result = new byte[length*2+2];
int i = 0;
for(int j = 0 ;j<chars.length;j++){
result[i++] = (byte)( (chars[j] & 0xFF00) >> 8 );
result[i++] = (byte)((chars[j] & 0x00FF)) ;
}
return result;
}
Utilisation:
String sample = "abc";
//First get the chars of the String,each char has two bytes(Java).
Char[] sample_chars = sample.toCharArray();
//Get the bytes
byte[] result = charArray2ByteArray(sample_chars).
//Back to String.
//Make sure we use UTF_16BE. Because we read the memory of Unicode of
//the String from Left to right. That's the same reading
//sequece of UTF-16BE.
String sample_back= new String(result , StandardCharsets.UTF_16BE);