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?

36
demandé sur Scott 2011-02-08 13:25:05

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.

12
répondu Jon Skeet 2011-02-08 10:29:11

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);
14
répondu Glen Best 2016-08-29 13:04:47

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.

6
répondu Cassian 2018-09-22 17:00:27

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

4
répondu Paŭlo Ebermann 2011-02-08 10:50:29

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);
4
répondu Peter Lawrey 2014-03-12 23:07:31

vous devriez utiliser getBytes() au lieu de toCharArray()

Remplacer la ligne

char[] password = "password".toCharArray();

byte[] password = "password".getBytes();
2
répondu yoda 2012-10-11 14:42:10

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.

2
répondu Vit Khudenko 2014-03-12 10:40:21

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);
1
répondu junqiang chen 2016-01-15 06:21:52