簡體   English   中英

將char數組轉換為字節數組並再次返回

[英]Converting char array into byte array and back again

我正在尋找將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));

斷言總是失敗(並且,關鍵是,當在生產中使用代碼時,密碼被拒絕),但是打印語句打印出密碼三次。 為什么passwordBytes1AsStringpasswordBytes2AsString不同passwordAsString ,又出現相同? 我錯過了一個空終結符或什么? 我該怎么做才能使轉換和非轉換工作?

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

問題是你使用String(byte[])構造函數,它使用平台默認編碼。 這幾乎不是你應該做的 - 如果你傳入“UTF-16”作為字符編碼工作,你的測試可能會通過。 目前我懷疑passwordBytes1AsStringpasswordBytes2AsString每個長16個字符,其他每個字符都是U + 0000。

原始答案

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

這是StandardCharsetsJavaDoc頁面 請在JavaDoc頁面上注意這一點:

這些字符集保證在Java平台的每個實現中都可用。

如果你想使用ByteBuffer和CharBuffer,不要做簡單的.asCharBuffer() ,它只是一個UTF-16(LE或BE,取決於你的系統 - 你可以用order方法設置字節order )轉換(因為Java字符串,因此你的char[]內部使用這種編碼)。

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

將byte []轉換為String時,還應指明編碼(它應該是相同的編碼)。

我會做的是使用循環轉換為字節,另一個轉換回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);

你應該使用getBytes()而不是toCharArray()

更換線

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

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

這是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]范圍內的字符將受到影響。

當您在Java中使用字符串中的GetBytes時,返回結果將取決於您的計算機設置的默認編碼。(例如:StandardCharsetsUTF-8或StandardCharsets.ISO_8859_1etc ...)。

因此,無論何時您想從String對象中獲取。 確保提供編碼。 喜歡 :

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM