char []をbyte []に​​変換する


回答:


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

または

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

デフォルト以外の文字セットを取得します。

更新: Java 7以降:new String(ch).getBytes(StandardCharsets.UTF_8);


4
プラットフォームのデフォルトの文字セットを使用することは、ほとんどの場合間違っています(Webアプリ)。
maaartinus 2011

4
これは簡単な解決策です。新しい文字列を使用するため、操作に必要なスペースが2倍になります。非常に大きな入力ではうまく機能しません。
Levent Divilioglu 2018年

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

ソリューションは、パスワードをchar []に保存するというSwingの推奨事項から着想を得ています。(パスワードに文字列よりもchar []が優先される理由を参照してください)。

機密データをログに書き込まないようにし、JVMがそのデータへの参照を保持しないようにしてください。


上記のコードは正しいですが、効果的ではありません。パフォーマンスは必要ないがセキュリティが必要な場合は、それを使用できます。セキュリティも目標ではない場合は、単純に実行してくださいString.getBytesencodeJDKでの実装を見下ろすと、上記のコードは効果的ではありません。さらに、配列をコピーしてバッファを作成する必要があります。変換する別の方法は、すべてのコードビハインドをインライン化することですencodeUTF-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オブジェクトよりもコストが安いと思いますか?
Andi Jay

15
@CrazyJayこのメソッドは「chars」を文字列プールに保存しないと思います。このようにして、パスワードデータをより安全に操作できます。
Andrii Nemchenko 2012

1
@Cassianメソッドが正しく機能しません。読むにはここで詳細stackoverflow.com/a/20604909/355491を
Andrii Nemchenko

1
@Prabsいいえ、1つのUTF-8文字は1〜4バイトかかります。1つのASCII文字でも8ビットを使用します。
Andrii Nemchenko 2018

1
この 'toBytes()'メソッドには重要な副作用があります。入力文字を消去します。charBuffer.array()は、実際には入力文字です。Arrays.fill()は、実際には入力を消去します。多くの場合は問題ありませんが、望ましくない効果が生じる場合があります。
光良2018年

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は実装固有でしょうか?NetBeans 7.4で1.7_51を使用していますが、後続の\ 0に気づいていません。

@orthopteroidはい、この例はjvm固有である可能性があります。これは、oracle 1.7.0_45 linux 64ビット(メモリから)で実行されました。次の実装(grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/…)ではaverageBytesPerChar()、1以外を返すとエラーが発生し ます(1.1を取得します)。興味深いことに、oracle1.7.0_51とopenjdk1.7.0_51を再確認したところ、10文字で壊れていることがわかったため、どのOS / archを使用していますか。
djsutho 2014年

@アンドレイ心配ありません。関数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であり、1文字あたり最大4バイトである可能性があるため、この回答は正しくありません(さらに多くの可能性がありますが、実際には最大4バイトしか見つかりませんでした)。各文字から1バイトを取得するだけでは、非常に限られた文字セットでのみ機能します。joelonsoftware.com/articles/Unicode.htmlの「絶対最小すべてのソフトウェア開発者は絶対に、積極的にUnicodeと文字セットについて知っておく必要があります(言い訳はありません!)」をお読みください
Ilane 2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.