Я хотів би перетворити масив символів на байтовий масив у Java. Які методи існують для здійснення цього перетворення?
Я хотів би перетворити масив символів на байтовий масив у Java. Які методи існують для здійснення цього перетворення?
Відповіді:
char[] ch = ?
new String(ch).getBytes();
або
new String(ch).getBytes("UTF-8");
щоб отримати кодировку, яка не є типовою.
Оновлення: З Java 7:new String(ch).getBytes(StandardCharsets.UTF_8);
Перетворення без створення String
об’єкта:
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;
}
Використання:
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
Рішення натхнене рекомендацією Swing зберігати паролі у символі []. (Див. Чому для паролів перевага надається символу char [] перед рядком? )
Пам’ятайте, що не слід записувати конфіденційні дані до журналів і переконайтесь, що JVM не буде містити посилань на них.
Наведений вище код правильний, але неефективний. Якщо вам не потрібна продуктивність, але вам потрібна безпека, ви можете її використовувати. Якщо безпека також не є метою, тоді робіть просто String.getBytes
. Наведений вище код не ефективний, якщо ви дивитесь на імплементацію encode
JDK. Крім того, вам потрібно скопіювати масиви та створити буфери. Інший спосіб перетворення - це вбудований весь код позаду encode
(приклад для 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"))
Вибачте, що використовую мову Scala. Якщо у вас є проблеми з перетворенням цього коду на Java, я можу його переписати. А щодо продуктивності завжди перевіряйте реальні дані (наприклад, з JMH). Цей код виглядає дуже схожим на те, що ви можете бачити в JDK [ 2 ] та Protobuf [ 3 ].
Відповідь Андрія (найвища оцінка на момент написання статті) дещо неправильна. Я б додав це як коментар, але я недостатньо авторитетний.
У відповідь Андрія:
char[] chars = {'c', 'h', 'a', 'r', 's'}
byte[] bytes = Charset.forName("UTF-8").encode(CharBuffer.wrap(chars)).array();
виклик array () може не повернути бажаного значення, наприклад:
char[] c = "aaaaaaaaaa".toCharArray();
System.out.println(Arrays.toString(Charset.forName("UTF-8").encode(CharBuffer.wrap(c)).array()));
вихід:
[97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 0]
Як бачимо, додано нульовий байт. Щоб уникнути цього, використовуйте наступне:
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));
вихід:
[97, 97, 97, 97, 97, 97, 97, 97, 97, 97]
Оскільки відповідь також натякає на використання паролів, можливо, варто очистити масив, який підтримує ByteBuffer (доступ до якого здійснюється через функцію 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()
повернете щось, окрім 1 (я отримую 1.1). З цікавості, яку ОС / арку ви використовуєте, оскільки я двічі перевірив за допомогою oracle 1.7.0_51 та openjdk 1.7.0_51 і виявив, що він зламаний на 10 символів.
buffer.array()
в toBytes
функції по- , як і раніше повинна бути перевизначений, в даний час тільки копія.
Ви можете зробити метод:
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;
}
Сподіваюся, це допомагає