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

时间:2011-02-08 10:25:06

标签: java arrays char byte

我希望将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));

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

8 个答案:

答案 0 :(得分: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

<强>除了:

java.nio classes和java.io Reader / Writer类使用ByteBuffer&amp; 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);

答案 1 :(得分:12)

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

答案 2 :(得分: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());    
    }

已编辑使用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());    
}

这是JavaDoc page for StandardCharsets。 请注意JavaDoc页面:

  

这些charset保证在Java平台的每个实现中都可用。

答案 3 :(得分:4)

如果你想使用ByteBuffer和CharBuffer,不要做简单的.asCharBuffer(),它只是一个UTF-16(LE或BE,取决于你的系统 - 你可以设置字节顺序order方法)转换(因为Java字符串,因此您的char[]内部使用此编码)。

使用Charset.forName(charsetName),然后使用encodedecode方法,或newEncoder / newDecoder

将byte []转换为String时,还应指明编码(它应该是相同的编码)。

答案 4 :(得分:4)

我会做的是使用循环转换为字节而另一个循环转换回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);

答案 5 :(得分:2)

您应该使用getBytes()代替toCharArray()

替换

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

byte[] password = "password".getBytes();

答案 6 :(得分:2)

这是Peter Lawrey的答案的延伸。为了向后(字节到字符)转换正确地为整个字符范围工作,代码应该如下:

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));
}

在使用(& 0xff)之前,我们需要“取消签名”字节。否则,所有可能的char值中的一半将无法正确返回。例如,[0x80..0xff]范围内的字符会受到影响。

答案 7 :(得分:1)

当您在Java中使用字符串中的GetBytes时,返回结果将取决于您的计算机设置的默认编码。(例如:StandardCharsetsUTF-8或StandardCharsets.ISO_8859_1etc ...)。

因此,无论何时你想从String对象中获取getBytes。确保提供编码。喜欢:

String sample = "abc";
Byte[] a_byte = sample .getBytes(StandardCharsets.UTF_8);

让我们检查代码发生了什么。 在java中,名为sample的String由Unicode存储。 String中的每个char都以2个字节存储。

sample :  value: "abc"   in Memory(Hex):  00 61 00 62 00 63
        a -> 00 61
        b -> 00 62
        c -> 00 63

但是,当我们从字符串中获取字符时,我们有

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

为了获取String的oringle字节。我们可以只读取字符串的内存并获取String.Below的每个字节是示例代码:

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;
}

用途:

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