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ステートメントはパスワードを3回出力します。なぜpasswordBytes1AsString
およびpasswordBytes2AsString
passwordAsString
とは異なるが、同じように見える? nullターミネーターなどがありませんか?変換と変換解除を機能させるにはどうすればよいですか?
問題は、プラットフォームのデフォルトのエンコーディングを使用するString(byte[])
コンストラクターの使用です。それはほとんどneverすべきことです-動作する文字エンコーディングとして "UTF-16"を渡せば、テストはおそらくパスします。現在、私はpasswordBytes1AsString
およびpasswordBytes2AsString
はそれぞれ16文字で、他のすべての文字はU + 0000です。
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);
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());
}
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());
}
StandardCharsetsのJavaDocページ です。 JavaDocページでこれに注意してください。
これらの文字セットは、Javaプラットフォームのすべての実装で使用できることが保証されています。
ByteBufferとCharBufferを使用する場合は、単純な.asCharBuffer()
を実行しないでください。UTF-16(システムに応じてLEまたはBE-を実行します。 order
メソッド)変換(Java文字列、したがって_char[]
_は内部的にこのエンコーディングを使用するため))。
Charset.forName(charsetName)
を使用してから、そのencode
またはdecode
メソッド、またはnewEncoder
/newDecoder
を使用します。
Byte []をStringに変換するときは、エンコーディングも指定する必要があります(同じエンコーディングである必要があります)。
ループを使用してバイトに変換し、もう1つを使用して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);
これは、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]
の範囲内の文字が影響を受けます。
getBytes()
の代わりにtoCharArray()
を使用する必要があります
行を置き換える
char[] password = "password".toCharArray();
と
byte[] password = "password".getBytes();
Javaで文字列からGetBytesを使用する場合、返される結果は、コンピューター設定のデフォルトのエンコードに依存します(例:StandardCharsetsUTF-8またはStandardCharsets.ISO_8859_1etc ...)。
したがって、文字列オブジェクトからBytesを取得したいときはいつでも。必ずencodeを指定してください。のような:
String sample = "abc";
Byte[] a_byte = sample .getBytes(StandardCharsets.UTF_8);
コードで何が起こったのかを確認しましょう。 Javaでは、sampleという名前の文字列はUnicodeで保存されます。 String内のすべての文字が2バイトで保存されます。
sample : value: "abc" in Memory(Hex): 00 61 00 62 00 63
a -> 00 61
b -> 00 62
c -> 00 63
しかし、文字列からBytesを取得すると、
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.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);