バイト配列を文字列に変換する(Java)


85

Google AppEngineでウェブアプリケーションを書いています。これにより、基本的.htmlに、blobstoreにファイルとして保存されるhtmlコードを編集できます。

私はfetchDataを使用byte[]して、ファイル内のすべての文字のを返します。ユーザーがHTMLコードを編集できるように、HTMLに印刷しようとしています。すべてがうまくいきます!

これが私の唯一の問題です:

文字列に戻すときに、バイト配列にいくつかの問題があります。スマートな引用といくつかのキャラクターがファンキーに見えます。(?や日本語の記号など)具体的には、問題の原因となっているのは負の値を持つ数バイトです。

スマート引用符は、として戻って来ている-108-109バイト配列に。これはなぜですか?また、負のバイトをデコードして正しい文字エンコードを表示するにはどうすればよいですか?



こんにちは、私はそれが本当に古い投稿であることを知っていますが、私は同様の問題に直面しています。私はSSLのman-in-the-middleプロキシを作成しています。私が直面している問題はあなたと同じです。私はソケットに耳を傾け、にデータを取得InputStreamし、その後にbyte[]byte[]を文字列に変換しようとすると(攻撃には応答本文を使用する必要があります)、スマートな引用符や疑問符などでいっぱいの本当に面白い文字が表示されます。私はあなたの問題は、私たちの両方を扱っていると私のと同じであると考えているhtmlの中でbyte[]。アドバイスをいただけますか?
パルルS 2014

ちなみに、Sytem.propertiesを使用してシステムのエンコーディングを見つけたところ、「Cp1252」であることがわかりました。今、私は使用しましたString str=new String(buffer, "Cp1252");が、助けにはなりませんでした。
パルルS 2014

回答:


141

バイト配列には、特別なエンコーディングの文字が含まれています(知っておく必要があります)。文字列に変換する方法は次のとおりです。

String decoded = new String(bytes, "UTF-8");  // example for one encoding type

ちなみに、Javaデータ型byteが署名されているという理由だけで、生のバイトが負の小数として表示される場合があります。これは、-128〜127の範囲をカバーします。


-109 = 0x93: Control Code "Set Transmit State"

値(-109)は、UNICODEの印刷不可能な制御文字です。したがって、UTF-8はその文字ストリームの正しいエンコーディングではありません。

0x93「Windows-1252」では、探している「スマートクォート」であるため、そのエンコーディングのJava名は「Cp1252」です。次の行はテストコードを提供します:

System.out.println(new String(new byte[]{-109}, "Cp1252")); 

5
UTF-8を使用してみましたが、それでも?として表示されました。これらの負の値のマッピングが見つからないのはなぜですか?
Josh

0x93はUTF-8の有効な継続バイトですが、そのバイトが存在すると、最初の2ビットが設定されたバイトの後にない場合にのみUTF-8であることが除外されます。
ニックジョンソン

1
@Josh Andreasが理由を説明します-Javaのbyteデータ型が署名されているためです。「負の」値は、最上位バイトが設定されたバイトです。彼はまた、使用すべき最も可能性の高い文字セットはWindows-1252であると説明しています。ただし、推測することなく、コンテキストまたは規則から使用する文字セットを知っておく必要があります。
ニックジョンソン

25

Java7以降

StandardCharsetsから定数Stringとして、希望のエンコーディングをコンストラクターに渡すこともできます。これは、他の回答で示唆されているように、エンコーディングをとして渡すよりも安全な場合があります。CharsetString

たとえば、UTF-8エンコーディングの場合

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

1
これは、2011年からの回答の繰り返しである-1
james.garriss

2
@ james.garriss Java 7で導入された、エンコーディングを定数として渡すことができる新しいコンストラクターについて言及している限り、そうではないと思います。これは、以前のAPIよりも優れていて安全だと思います。エンコードが文字列として渡された場合は、以前の回答で言及されています。
davnicwil 2015


5
public class Main {

    /**
     * Example method for converting a byte to a String.
     */
    public void convertByteToString() {

        byte b = 65;

        //Using the static toString method of the Byte class
        System.out.println(Byte.toString(b));

        //Using simple concatenation with an empty String
        System.out.println(b + "");

        //Creating a byte array and passing it to the String constructor
        System.out.println(new String(new byte[] {b}));

    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        new Main().convertByteToString();
    }
}

出力

65
65
A

5
public static String readFile(String fn)   throws IOException 
{
    File f = new File(fn);

    byte[] buffer = new byte[(int)f.length()];
    FileInputStream is = new FileInputStream(fn);
    is.read(buffer);
    is.close();

    return  new String(buffer, "UTF-8"); // use desired encoding
}

3
read例外をスローすると、このコードはリソースをリークします。
レドワルド2015

4

私は提案します Arrays.toString(byte_array);

それはあなたの目的に依存します。たとえば、デバッグ時に表示される形式とまったく同じようにバイト配列を保存したいのですが、次のように[1, 2, 3]なりArrays.toString (byte_array)ます。バイトを文字形式に変換せずにまったく同じ値を保存したい場合は、これを実行します。ただし、バイトではなく文字を保存する場合は、を使用する必要がありますString s = new String(byte_array)。この場合、s[1, 2, 3]文字の形式と同等です。


これを提案している理由について詳しく教えてください。(それは問題を解決しますか?それがそれを解決する理由を言うことができますか?)ありがとう!
ディーンJ

それはあなたの目的に依存します。たとえば、デバッグ時に表示される形式とまったく同じようにバイト配列を保存したいのですが、次のようになります。[1、2、3]バイトを文字形式に変換せずにまったく同じ値を保存したい場合は、 Arrays.toString(byte_array)はこれを行います。ただし、バイトではなく文字を保存する場合は、String s = new String(byte_array)を使用する必要があります。この場合、sは文字の形式で[1、2、3]と同等です。
質問者

@sas、コメントとしてではなく、(編集して)回答自体にこの情報を追加する必要があります。一般的にSOでは、コメントはいつでも削除される可能性があることに常に留意する必要があります。する必要があります。本当に重要な情報は回答自体に含まれている必要があります。
Jeen Broekstra 2015年

3

Andreas_Dからの前の答えは良いです。出力を表示している場所にはフォントと文字エンコードがあり、一部の文字をサポートしていない可能性があることを追加します。

問題となっているのがJavaなのかディスプレイなのかを判断するには、次のようにします。

    for(int i=0;i<str.length();i++) {
        char ch = str.charAt(i);
        System.out.println(i+" : "+ch+" "+Integer.toHexString(ch)+((ch=='\ufffd') ? " Unknown character" : ""));
    }

Javaは、理解できない文字を、不明な文字の公式文字0xfffdにマップします。'?'が表示された場合 出力では、0xfffdにマップされていませんが、問題となっているのは表示フォントまたはエンコーディングであり、Javaではありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.