Java ByteBufferからStringへ


121

これは、この方法でByteBufferをStringに変換する正しいアプローチですか?

String k = "abcd";
ByteBuffer b = ByteBuffer.wrap(k.getBytes());
String v = new String(b.array());

if(k.equals(v))
    System.out.println("it worked");
else
    System.out.println("did not work");

私が尋ねる理由は、これは単純すぎるように見えるが、Javaのような他のアプローチ:ByteBufferとの間の文字列の変換および関連する問題はより複雑に見えるためです。


3
さて、やってみましたか?
tckmn 2013年

6
はい、私はそれをしました、そしてそれは働きます。しかし、stackoverflow.com
questions / 1252468 /…

1
@Doorknob et。al。彼はエンコーディングが欠けており、例(構文が修正された場合)は機能しますが、彼の方法はまだ正しくありません。
Gus

回答:


83

編集(2018): @xinyongChengによる編集された兄弟の回答はより単純なアプローチであり、受け入れられる回答でなければなりません。

バイトがプラットフォームのデフォルトの文字セットに含まれていることがわかっている場合、このアプローチは妥当です。あなたの例では、これは本当ですk.getBytes()は、プラットフォームのデフォルトの文字セットでバイトを返すです。

より頻繁には、エンコーディングを指定する必要があります。ただし、リンクした質問よりも簡単な方法があります。String APIは、特定のエンコーディングでStringとbyte []配列の間で変換するメソッドを提供します。これらのメソッドは、「デコード[エンコード]プロセスをさらに制御する必要がある場合」に CharsetEncoder / CharsetDecoderを使用することを推奨しています。

特定のエンコーディングで文字列からバイトを取得するには、兄弟のgetBytes()メソッドを使用できます。

byte[] bytes = k.getBytes( StandardCharsets.UTF_8 );

特定のエンコーディングのバイトを文字列に入れるには、別の文字列コンストラクタを使用できます。

String v = new String( bytes, StandardCharsets.UTF_8 );

ByteBuffer.array()任意のオペレーションです。ByteBufferを配列で作成した場合は、その配列を直接使用できます。それ以外の場合、安全を確保したい場合は、を使用ByteBuffer.get(byte[] dst, int offset, int length)してバッファからバイト配列にバイトを取得します。


そしてByteBuffer.get関数では、入力はバイトの配列ですが、どうすれば取得できますか?もう一度k.getbytesと言っても意味がありませんか?
William Kinaan

@WilliamKinaan-フィードしたbyte []がありますByteBuffer.get(byte[] dst, int offset, int length)。String()コンストラクター `String(byte [] bytes、int offset、int length、Charset charset)を使用して、それからStringを構築できます。両方の呼び出しに同じオフセットと長さの値を使用できます。
アンディトーマス

java.nio.ByteBufferにはk.getBytes()メソッドがありません(使用しているバージョンにはない場合があります)。したがって、byte []を返すk.array()メソッドを使用しました。
Madura Pradeep

@MaduraPradeep-質問とこの回答のコード例kでは、ByteBufferではなくStringです。
Andy Thomas

バイトを文字列に、またはその逆に変換するには、UTF-8が最適な文字セットではない場合があることに注意してください。文字バイトの1対1のマッピングのためによく見える、ISO-8859-1を使用しstackoverflow.com/questions/9098022/...
asmaier

102

復号するための簡単な方法があるByteBufferにはStringアンディ・トーマスが言及したすべての問題、なしでは。

String s = StandardCharsets.UTF_8.decode(byteBuffer).toString();

2
バイトを文字列に、またはその逆に変換するには、UTF-8が最適な文字セットではない場合があることに注意してください。バイトから文字への1対1のマッピングについては、ISO-8859-1を使用することをお勧めします。stackoverflow.com/ questions / 9098022 /…を参照してください。
2017年

また、実際には文字列を必要としないので、CharBuffer decode()戻り値はCharSequence(のようにString)なので、余分なコピーを避けて直接使用できます。
David Ehrmann、2018

15

これを試して:

new String(bytebuffer.array(), "ASCII");

NB。エンコーディングを知らないと、バイト配列を文字列に正しく変換できません。

これが役に立てば幸い


10
UTF-8は、ASCIIよりもおそらくより良いデフォルトの推測でしょうか?
Gus

3
OPがプラットフォームのデフォルトの文字セットを使用するk.getBytes()を使用する場合は、どちらも指定しないでください。
アンディトーマス

7
すべてのバッファが配列に対応しているわけではないため.array()、例外がスローされる場合があります。
Dzmitry Lazerka 2016年

すべてのバイトバッファがこの.array()メソッドをサポートしているわけではありません。
ScalaWilliam

3
気をつけて!を使用する場合、を使用して配列の正しい位置から開始array()する必要がありますarrayOffset()!通常、arrayOffset()は0なので、これは微妙な落とし穴です。しかし、そうでないまれなケースでは、考慮しないと見つけにくいバグが発生します。
オリバー

13

指摘したいだけですが、ByteBuffer.array()が常に機能すると想定するのは安全ではありません。

byte[] bytes;
if(buffer.hasArray()) {
    bytes = buffer.array();
} else {
    bytes = new byte[buffer.remaining()];
    buffer.get(bytes);
}
String v = new String(bytes, charset);

通常、buffer.hasArray()は、ユースケースに応じて常にtrueまたはfalseになります。実際には、どのような状況でも実際に機能させたいのでない限り、不要なブランチを最適化することは安全です。しかし、残りの回答は、ByteBuffer.allocateDirect()で作成されたByteBufferでは機能しない場合があります。


バッファがByteBuffer.wrap(bytes, offset, size)ファクトリ.array()を介して作成された場合、bytes配列全体が返されます。xinyong Chengが提案したフォームを使用することをお勧めします
Lev Kuznetsov

Charsetの.decode()はより良いソリューションであると合意されています。私の回答のコンテキストは有用な情報だと思いますが、今はそうではありません。
Fuwjax 2017

2
気をつけて!あなたが使用している場合はarray()、あなたがしなければならないも使用arrayOffset()配列内の正しい位置に開始するように!通常、arrayOffset()は0なので、これは微妙な落とし穴です。しかし、そうでないまれなケースでは、考慮しないと見つけにくいバグが発生します。
オリバー

8

単純な呼び出しに関する回答array()は完全に正しくありません。バッファが部分的に消費されている場合、または配列の一部を参照している場合(ByteBuffer.wrap必ずしも最初からではなく、特定のオフセットで配列を使用できます)、私たちの計算ではそれ。これは、すべての場合にバッファーに対して機能する一般的なソリューションです(エンコードはカバーしていません)。

if (myByteBuffer.hasArray()) {
    return new String(myByteBuffer.array(),
        myByteBuffer.arrayOffset() + myByteBuffer.position(),
        myByteBuffer.remaining());
} else {
    final byte[] b = new byte[myByteBuffer.remaining()];
    myByteBuffer.duplicate().get(b);
    return new String(b);
}

エンコーディングに関する懸念事項については、Andy Thomasの回答を参照してください。


1

(エンコードの問題は別として)リンクされたより複雑なコードの一部は、単純にすべてのバイトをエンコードするのではなく、問題のByteBufferの「アクティブ」部分を(たとえば、位置と制限を使用して)取得するという問題に直面することに注意してください。 (これらの回答の例の多くが行うように)バッキング配列全体で。


1

StringをByteBufferに変換し、次にJavaを使用してByteBufferからStringに戻します。

import java.nio.charset.Charset;
import java.nio.*;

String babel = "obufscate thdé alphebat and yolo!!";
System.out.println(babel);
//Convert string to ByteBuffer:
ByteBuffer babb = Charset.forName("UTF-8").encode(babel);
try{
    //Convert ByteBuffer to String
    System.out.println(new String(babb.array(), "UTF-8"));
}
catch(Exception e){
    e.printStackTrace();
}

これは、印刷された裸の文字列を最初に印刷し、次にByteBufferをarray()にキャストします。

obufscate thdé alphebat and yolo!!
obufscate thdé alphebat and yolo!!

また、これは私にとっても役に立ちました。文字列をプリミティブバイトに減らすと、何が起こっているのかを調べるのに役立ちます。

String text = "こんにちは";
//convert utf8 text to a byte array
byte[] array = text.getBytes("UTF-8");
//convert the byte array back to a string as UTF-8
String s = new String(array, Charset.forName("UTF-8"));
System.out.println(s);
//forcing strings encoded as UTF-8 as an incorrect encoding like
//say ISO-8859-1 causes strange and undefined behavior
String sISO = new String(array, Charset.forName("ISO-8859-1"));
System.out.println(sISO);

文字列をUTF-8として解釈し、次にISO-8859-1として再び出力します。

こんにちは
ããã«ã¡ã¯

1

この質問の根本は、バイトを文字列にデコードする方法ですか?

これは、JAVA NIO CharSetで実行できます。

public final CharBuffer decode(ByteBuffer bb)

FileChannel channel = FileChannel.open(
  Paths.get("files/text-latin1.txt", StandardOpenOption.READ);
ByteBuffer buffer = ByteBuffer.allocate(1024);
channel.read(buffer);

CharSet latin1 = StandardCharsets.ISO_8859_1;
CharBuffer latin1Buffer = latin1.decode(buffer);

String result = new String(latin1Buffer.array());
  • まず、チャネルを作成し、それをバッファーで読み取ります
  • 次に、decodeメソッドはLatin1バッファーをcharバッファーにデコードします
  • 次に、結果を例えば文字列に入れることができます

コードがlatin1からutf8にデコードされていません。コードは正しいですが、CharBuffer utf8Bufferを呼び出すと、エンコーディングがないため、誤解を招く恐れがあります。
ビョルンLindqvist

0
private String convertFrom(String lines, String from, String to) {
    ByteBuffer bb = ByteBuffer.wrap(lines.getBytes());
    CharBuffer cb = Charset.forName(to).decode(bb);
    return new String(Charset.forName(from).encode(cb).array());
};
public Doit(){
    String concatenatedLines = convertFrom(concatenatedLines, "CP1252", "UTF-8");
};
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.