将char []转换为byte []


Answers:


76
char[] ch = ?
new String(ch).getBytes();

要么

new String(ch).getBytes("UTF-8");

获取非默认字符集。

更新:从Java 7开始:new String(ch).getBytes(StandardCharsets.UTF_8);


4
在大多数情况下(网络应用程序),使用平台的默认字符集是错误的。
maaartinus 2011年

4
这是一个简单的解决方案,因为使用了新的String,因此该操作所需的空间增加了一倍。对于非常大的输入,它将不能很好地工作。
Levent Divilioglu

167

转换而不创建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 []中。(请参阅为什么使用char []而不是String作为密码?

请记住不要将敏感数据写入日志,并确保JVM不会保留对其的任何引用。


上面的代码是正确的,但无效。如果您不需要性能但想要安全性,则可以使用它。如果安全也不是目标,那就干脆做String.getBytes。如果您不了解encodeJDK的实现,则上面的代码无效。此外,您需要复制数组并创建缓冲区。转换的另一种方法是内联所有代码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 ]中的代码非常相似。


这不会创建ByteBuffer吗?我猜这比String对象便宜吗?
安迪·杰伊

15
@CrazyJay我相信这种方法不会在字符串池中存储“字符”。这样,您可以更安全地使用密码数据。
安德里·尼姆琴科

1
@Cassian您的方法无法正常工作。在此处阅读详细信息stackoverflow.com/a/20604909/355491
安德里·内姆琴科

1
@Prabs不,一个UTF-8字符占用1到4个字节。甚至一个ASCII字符也需要8位。
安德里·尼姆琴科

1
此“ toBytes()”方法具有重要的副作用。它擦除输入字符。charBuffer.array()实际上是输入字符。Arrays.fill()实际上会清除输入。在许多情况下都可以,但有时会产生不良效果。
广良

19

编辑:安德烈的答案已更新,因此以下不再适用。

安德烈的答案(在撰写本文时票数最高)是不正确的。我会将其添加为评论,但我的声誉不足。

在安德烈的回答中:

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

尾随\ 0是否特定于实现?我将1.7_51与netbeans 7.4一起使用,却没有注意到尾随\ 0。

@orthopteroid是的,此示例可能是特定于jvm的。这是使用oracle 1.7.0_45 linux 64位(从内存)运行的。通过以下实现(grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/…),如果averageBytesPerChar()返回非1(我得到1.1),则会出现错误 。出于兴趣,您正在使用什么操作系统/架构,因为我用oracle 1.7.0_51和openjdk 1.7.0_51进行了两次检查,发现它被破坏了10个字符。
djsutho 2014年

@Andrey不用担心。注意,buffer.array()toBytes函数中仍然需要重写,当前只有副本。
djsutho 2014年

@Andrey我已经编辑了答案以反映所做的更改。
djsutho 2014年

@djsutho今天,我的平台是Windows7x64。抱歉,无法显示代码-我正在使用“ System.arraycopy(str.getBytes(“ UTF-8”),0,stor,0,used);“之类的代码。现在。

0
private static byte[] charArrayToByteArray(char[] c_array) {
        byte[] b_array = new byte[c_array.length];
        for(int i= 0; i < c_array.length; i++) {
            b_array[i] = (byte)(0xFF & (int)c_array[i]);
        }
        return b_array;
}

-5

您可以制作一个方法:

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

希望这可以帮助


4
这个答案是不正确的,因为char数据是Unicode,因此每个字符最多可以有4个字节(可能更多,但是在现实生活中,我最多只能找到4个字节)。仅从每个字符中提取一个字节仅适用于非常有限的字符集。请阅读joelonsoftware.com/articles/Unicode.html上的“每个软件开发人员绝对绝对肯定地了解Unicode和字符集(无借口!)” 。
伊兰(Ilane)2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.