Chciałbym przekonwertować tablicę znaków na tablicę bajtów w Javie. Jakie są metody dokonywania tej konwersji?
Chciałbym przekonwertować tablicę znaków na tablicę bajtów w Javie. Jakie są metody dokonywania tej konwersji?
Odpowiedzi:
char[] ch = ?
new String(ch).getBytes();
lub
new String(ch).getBytes("UTF-8");
aby uzyskać inny niż domyślny zestaw znaków.
Aktualizacja: od wersji Java 7:new String(ch).getBytes(StandardCharsets.UTF_8);
Konwertuj bez tworzenia Stringobiektu:
import java.nio.CharBuffer;
import java.nio.ByteBuffer;
import java.util.Arrays;
byte[] toBytes(char[] chars) {
CharBuffer charBuffer = CharBuffer.wrap(chars);
ByteBuffer byteBuffer = Charset.forName("UTF-8").encode(charBuffer);
byte[] bytes = Arrays.copyOfRange(byteBuffer.array(),
byteBuffer.position(), byteBuffer.limit());
Arrays.fill(byteBuffer.array(), (byte) 0); // clear sensitive data
return bytes;
}
Stosowanie:
char[] chars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
byte[] bytes = toBytes(chars);
/* do something with chars/bytes */
Arrays.fill(chars, '\u0000'); // clear sensitive data
Arrays.fill(bytes, (byte) 0); // clear sensitive data
Rozwiązanie zostało zainspirowane zaleceniem Swing dotyczącym przechowywania haseł w char []. (Zobacz Dlaczego hasła char [] są preferowane zamiast ciągów znaków? )
Pamiętaj, aby nie zapisywać wrażliwych danych w dziennikach i upewnij się, że JVM nie będzie zawierał żadnych odniesień do nich.
Powyższy kod jest poprawny, ale nieskuteczny. Jeśli nie potrzebujesz wydajności, ale chcesz bezpieczeństwa, możesz go użyć. Jeśli bezpieczeństwo również nie jest celem, zrób to po prostu String.getBytes. Powyższy kod nie jest skuteczny, jeśli spojrzysz na implementację encodew JDK. Poza tym musisz kopiować tablice i tworzyć bufory. Innym sposobem konwersji jest wbudowany cały kod za encode(na przykład dla UTF-8 ):
val xs: Array[Char] = "A ß € 嗨 𝄞 🙂".toArray
val len = xs.length
val ys: Array[Byte] = new Array(3 * len) // worst case
var i = 0; var j = 0 // i for chars; j for bytes
while (i < len) { // fill ys with bytes
val c = xs(i)
if (c < 0x80) {
ys(j) = c.toByte
i = i + 1
j = j + 1
} else if (c < 0x800) {
ys(j) = (0xc0 | (c >> 6)).toByte
ys(j + 1) = (0x80 | (c & 0x3f)).toByte
i = i + 1
j = j + 2
} else if (Character.isHighSurrogate(c)) {
if (len - i < 2) throw new Exception("overflow")
val d = xs(i + 1)
val uc: Int =
if (Character.isLowSurrogate(d)) {
Character.toCodePoint(c, d)
} else {
throw new Exception("malformed")
}
ys(j) = (0xf0 | ((uc >> 18))).toByte
ys(j + 1) = (0x80 | ((uc >> 12) & 0x3f)).toByte
ys(j + 2) = (0x80 | ((uc >> 6) & 0x3f)).toByte
ys(j + 3) = (0x80 | (uc & 0x3f)).toByte
i = i + 2 // 2 chars
j = j + 4
} else if (Character.isLowSurrogate(c)) {
throw new Exception("malformed")
} else {
ys(j) = (0xe0 | (c >> 12)).toByte
ys(j + 1) = (0x80 | ((c >> 6) & 0x3f)).toByte
ys(j + 2) = (0x80 | (c & 0x3f)).toByte
i = i + 1
j = j + 3
}
}
// check
println(new String(ys, 0, j, "UTF-8"))
Przepraszam za używanie języka Scala. Jeśli masz problemy z konwersją tego kodu do Java mogę go przepisać. A co z wydajnością, zawsze sprawdzaj rzeczywiste dane (na przykład z JMH). Ten kod wygląda bardzo podobnie do tego, co można zobaczyć w JDK [ 2 ] i Protobuf [ 3 ].
Odpowiedź Andreya (najwięcej głosów w momencie pisania) jest nieco niepoprawna. Dodałbym to jako komentarz, ale nie mam wystarczającej reputacji.
W odpowiedzi Andreya:
char[] chars = {'c', 'h', 'a', 'r', 's'}
byte[] bytes = Charset.forName("UTF-8").encode(CharBuffer.wrap(chars)).array();
wywołanie funkcji array () może nie zwrócić żądanej wartości, na przykład:
char[] c = "aaaaaaaaaa".toCharArray();
System.out.println(Arrays.toString(Charset.forName("UTF-8").encode(CharBuffer.wrap(c)).array()));
wynik:
[97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 0]
Jak widać, dodano bajt zerowy. Aby tego uniknąć, użyj następujących:
char[] c = "aaaaaaaaaa".toCharArray();
ByteBuffer bb = Charset.forName("UTF-8").encode(CharBuffer.wrap(c));
byte[] b = new byte[bb.remaining()];
bb.get(b);
System.out.println(Arrays.toString(b));
wynik:
[97, 97, 97, 97, 97, 97, 97, 97, 97, 97]
Ponieważ odpowiedź dotyczyła również używania haseł, warto wyczyścić tablicę, która obsługuje ByteBuffer (dostęp do niej uzyskuje się za pośrednictwem funkcji array ()):
ByteBuffer bb = Charset.forName("UTF-8").encode(CharBuffer.wrap(c));
byte[] b = new byte[bb.remaining()];
bb.get(b);
blankOutByteArray(bb.array());
System.out.println(Arrays.toString(b));
averageBytesPerChar()zwróci cokolwiek innego niż 1 (otrzymam 1.1). Nie interesuje mnie, jakiego systemu operacyjnego / arch używasz, gdy dwukrotnie sprawdziłem z oracle 1.7.0_51 i openjdk 1.7.0_51 i stwierdziłem, że jest uszkodzony z 10 znakami.
buffer.array()w toBytesfunkcji nadal musi zostać zastąpiona, obecnie jest tylko kopia.
Możesz stworzyć metodę:
public byte[] toBytes(char[] data) {
byte[] toRet = new byte[data.length];
for(int i = 0; i < toRet.length; i++) {
toRet[i] = (byte) data[i];
}
return toRet;
}
Mam nadzieję że to pomoże