将char数组转换为字节数组并再次返回

Sco*_*ott 36 java arrays byte char

我正在寻找将Java char数组转换为字节数组而不创建中间数String,因为char数组包含密码.我查了几种方法,但它们似乎都失败了:

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));
Run Code Online (Sandbox Code Playgroud)

断言总是失败(并且,关键是,当在生产中使用代码时,密码被拒绝),但是打印语句打印出密码三次.为什么passwordBytes1AsStringpasswordBytes2AsString不同passwordAsString,但看起来相同?我错过了一个空终结符或什么?我该怎么做才能使转换和非转换工作?

Gle*_*est 14

char和byte之间的转换是字符集编码和解码.我更喜欢在代码中尽可能清楚.它并不意味着额外的代码量:

 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
Run Code Online (Sandbox Code Playgroud)

在旁边:

java.nio类和java.io Reader/Writer类使用ByteBuffer和CharBuffer(使用byte []和char []作为后备数组).因此,如果直接使用这些类,通常更可取.但是,您可以随时执行:

 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);
Run Code Online (Sandbox Code Playgroud)


Jon*_*eet 12

问题是您使用String(byte[])构造函数,它使用平台默认编码.这几乎不是你应该做的 - 如果你传入"UTF-16"作为字符编码工作,你的测试可能会通过.目前我怀疑passwordBytes1AsString并且passwordBytes2AsString每个字符长16个字符,其他每个字符都是U + 0000.


Cas*_*ian 6

原始答案

    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());    
    }
Run Code Online (Sandbox Code Playgroud)

编辑使用StandardCharsets

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());    
}
Run Code Online (Sandbox Code Playgroud)

这是StandardCharsetsJavaDoc页面.请在JavaDoc页面上注意这一点:

这些字符集保证在Java平台的每个实现中都可用.

  • 在char []或byte []中存储敏感数据后,您需要清除敏感数据,因为Andrii在此处使用了解http://stackoverflow.com/a/9670279/1582089 (3认同)

Pet*_*rey 5

我会做的是使用一个循环转换为字节和另一个转换回字符。

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);
Run Code Online (Sandbox Code Playgroud)