バイト配列を文字列に、またはその逆に変換する方法は?


247

Androidでバイト配列を文字列に変換する必要がありますが、バイト配列に負の値が含まれています。

その文字列をバイト配列に再度変換すると、取得する値は元のバイト配列の値とは異なります。

適切な変換を行うにはどうすればよいですか?変換に使用するコードは次のとおりです。

// Code to convert byte arr to str:
byte[] by_original = {0,1,-2,3,-4,-5,6};
String str1 = new String(by_original);
System.out.println("str1 >> "+str1);

// Code to convert str to byte arr:
byte[] by_new = str1.getBytes();
for(int i=0;i<by_new.length;i++) 
System.out.println("by1["+i+"] >> "+str1);

私はこの問題に行き詰まっています。


3
そもそも、なぜ任意のバイナリデータを文字列に変換しようとしているのですか?回答ですでに述べた文字セットの問題のほかに、これを行うと文字列を悪用しているという事実もあります。byte[]バイナリデータとStringテキストにを使用することの何が問題になっていますか?
ヨアヒムザウアー

8
@Joachim-文字列の保存などを実行できる外部ツールがある場合があります。その場合、バイト配列を(何らかの方法でエンコードされた)文字列に変換できるようにしたいとします。
James Moore

回答:


377

バイト配列にはエンコードが必要です。負の値がある場合、エンコードをASCIIにすることはできません。それを理解したら、次のコマンドを使用してバイトのセットを文字列に変換できます。

byte[] bytes = {...}
String str = new String(bytes, "UTF-8"); // for UTF-8 encoding

あなたが使用できるエンコーディングの束、中に文字セットクラスを見ありますSunののjavadocを


4
@MauricePerryはなぜ機能しないのか説明できますUTF-8か?
Asif Mushtaq

12
@UnKnownは、UTF-8が一部の文字を2または3バイトの文字列としてエンコードするためです。すべてのバイト配列が有効なUTF-8エンコード文字列であるとは限りません。ISO-8859-1の方が適しています。ここでは、各文字がバイトとしてエンコードされています。
モーリスペリー

1
これは機能する可能性がありますが、文字列コンストラクタの使用は絶対に避けてください。
hfontanez 2017年

:1つの文字(8859-1付き)と(nio.charset付き)なし例外処理に1つのバイトをマッピングするためにString str = new String(bytes, java.nio.charset.StandardCharsets.ISO_8859_1);
イマン

1
Java 1.7以降では、新しいString(bytes、StandardCharsets.UTF_8)を使用できます
ihebiheb

101

との間の「適切な変換」はbyte[]String使用するエンコーディングを明示的に示すことです。aで開始し、byte[]実際にテキストデータが含まれていない場合、「適切な変換」は行われませんStringsはテキストbyte[]用で、バイナリデータ用です。本当に賢明なことは、どうしても必要な場合を除いて、それらの間の変換を避けることです。

本当にStringバイナリデータを保持するためにを使用する必要がある場合、最も安全な方法はBase64エンコーディングを使用することです。



4
Base64とあなたは私の命を救った
mstzn

2
Base64エンコーディングで問題が解決しました。UTF-8はすべての入力で機能しませんでした
Al-Alamin '28

37

根本的な問題は(私が思うに)次のような文字セットを無意識のうちに使用していることです。

 bytes != encode(decode(bytes))

ある場合には。UTF-8は、このような文字セットの例です。具体的には、特定のバイトシーケンスはUTF-8では有効なエンコーディングではありません。UTF-8デコーダーがこれらのシーケンスのいずれかを検出した場合、問題のバイトを破棄するか、「そのような文字がない」ためのUnicodeコードポイントとしてデコードする可能性があります。当然、その後、文字をバイトとしてエンコードしようとすると、結果は異なります。

解決策は次のとおりです。

  1. 使用している文字エンコーディングを明確にしてください。つまりString.toByteArray、明示的な文字セットを持つStringコンストラクタとメソッドを使用します。
  2. バイトデータに適切な文字セットを使用してください...または、代わりに(すべてのバイトシーケンスが有効なUnicode文字にマップされる "Latin-1"など)。
  3. バイトが(本当に)バイナリデータであり、「テキストベースの」チャネルを介してそれらを送受信できるようにしたい場合は、この目的のために設計された Base64エンコーディングなどを使用します。

1
「Latin-1」エンコーディングを使用するためのヒントをありがとう!
ハメ撮り

31

String配列で新しいものを作成するだけです:http : //www.mkyong.com/java/how-do-convert-byte-array-to-string-in-java/

String s = new String(bytes);

結果の文字列のバイトは、使用する文字セットによって異なります。newString(bytes)およびnew String(bytes、Charset.forName( "utf-8"))およびnew String(bytes、Charset.forName( "utf-16"))は、String#を呼び出すと、すべて異なるバイト配列になりますgetBytes()(デフォルトの文字セットによる)


9
いいえ。結果の文字列のバイトは、使用する文字セットによって異なります。呼び出すnew String(bytes)new String(bytes, Charset.forName("utf-8"))new String(bytes, Charset.forName("utf-16"))すべてのバイト配列が異なりますString#getBytes()(デフォルトの文字セットによって異なります)
NS du Toit

1
誤解を招く。異なる方法でデコードするとchar、結果のs(および表示されるテキスト)が異なります。異なる入力を変換するため、デフォルトのエンコーディング(別の方法で指定するために使用)を使用してバイトに戻す変換は、必然的に異なります。文字列は、それらが作成されたものを格納しません。sはエンコーディングを持たず、それ以外の場合は格納しません。StringbytesString#getBytes("charset")byte[]charString
zapl 2016年

14

使用して使用new String(byOriginal)に戻すことは、2つの値が等しいことを保証byte[]するものでgetBytes()はありませんbyte[]。これは、への呼び出しにあるStringCoding.encode(..)エンコードであろうStringCharset.defaultCharset()。このエンコード中に、エンコーダーは不明な文字を置き換えることを選択し、その他の変更を行う場合があります。したがって、String.getBytes()コンストラクターに最初に渡したときと同じ配列を使用することはできません。


9

問題があった理由:誰かが既に指定したとおり: byte []で開始し、実際にテキストデータが含まれていない場合、「適切な変換」は行われません。文字列はテキスト用、byte []はバイナリデータ用であり、本当に賢明なことは、本当に必要な場合を除いて、文字列間の変換を回避することです。

PDFファイルからbyte []を作成し、それを文字列に変換してから、文字列を入力として受け取り、ファイルに戻すときに、この問題を観察していました。

したがって、エンコードとデコードのロジックが私と同じであることを確認してください。私は明示的にbyte []をBase64にエンコードし、それをデコードして再度ファイルを作成しました。

ユースケース: 原因はいくつかの制限に私が送信しようとしていたbyte[]中でrequest(POST)、次のようにプロセスがありました。

PDFファイル>> Base64.encodeBase64(byte [])>>文字列>>リクエストで送信(POST)>>文字列を受信>> Base64.decodeBase64(byte [])>>バイナリを作成

これを試してみて、これは私のために働いた。

File file = new File("filePath");

        byte[] byteArray = new byte[(int) file.length()];

        try {
            FileInputStream fileInputStream = new FileInputStream(file);
            fileInputStream.read(byteArray);

            String byteArrayStr= new String(Base64.encodeBase64(byteArray));

            FileOutputStream fos = new FileOutputStream("newFilePath");
            fos.write(Base64.decodeBase64(byteArrayStr.getBytes()));
            fos.close();
        } 
        catch (FileNotFoundException e) {
            System.out.println("File Not Found.");
            e.printStackTrace();
        }
        catch (IOException e1) {
            System.out.println("Error Reading The File.");
            e1.printStackTrace();
        }

6

これは私にとってはうまくいきます:

String cd="Holding some value";

文字列からバイトへの変換[]:

byte[] cookie = new sun.misc.BASE64Decoder().decodeBuffer(cd);

byte []から文字列への変換:

cd = new sun.misc.BASE64Encoder().encode(cookie);

5
private static String toHexadecimal(byte[] digest){
        String hash = "";
    for(byte aux : digest) {
        int b = aux & 0xff;
        if (Integer.toHexString(b).length() == 1) hash += "0";
        hash += Integer.toHexString(b);
    }
    return hash;
}

これは質問の答えにはなりません。
james.garriss 2015年

質問には答えませんが、役に立ちました+1
Lazy Ninja

5

答えにないものに気づきました。バイト配列の各バイトを文字にキャストして、char配列に入れることができます。次に、文字列は

new String(cbuf)
ここで、cbufはchar配列です。元に戻すには、各文字をバイトにキャストする文字列をループしてバイト配列に入れます。このバイト配列は最初のものと同じです。


public class StringByteArrTest {

    public static void main(String[] args) {
        // put whatever byte array here
        byte[] arr = new byte[] {-12, -100, -49, 100, -63, 0, -90};
        for (byte b: arr) System.out.println(b);
        // put data into this char array
        char[] cbuf = new char[arr.length];
        for (int i = 0; i < arr.length; i++) {
            cbuf[i] = (char) arr[i];
        }
        // this is the string
        String s = new String(cbuf);
        System.out.println(s);

        // converting back
        byte[] out = new byte[s.length()];
        for (int i = 0; i < s.length(); i++) {
            out[i] = (byte) s.charAt(i);
        }
        for (byte b: out) System.out.println(b);
    }

}

2

javax.xml.bind.DatatypeConverter それを行う必要があります:

byte [] b = javax.xml.bind.DatatypeConverter.parseHexBinary("E62DB");
String s = javax.xml.bind.DatatypeConverter.printHexBinary(b);

2

バイトの配列を文字列に変換するいくつかのメソッドを次に示します。それらがうまく機能することをテストしました。

public String getStringFromByteArray(byte[] settingsData) {

    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(settingsData);
    Reader reader = new BufferedReader(new InputStreamReader(byteArrayInputStream));
    StringBuilder sb = new StringBuilder();
    int byteChar;

    try {
        while((byteChar = reader.read()) != -1) {
            sb.append((char) byteChar);
        }
    }
    catch(IOException e) {
        e.printStackTrace();
    }

    return sb.toString();

}

public String getStringFromByteArray(byte[] settingsData) {

    StringBuilder sb = new StringBuilder();
    for(byte willBeChar: settingsData) {
        sb.append((char) willBeChar);
    }

    return sb.toString();

}

2

たとえ

new String(bytes, "UTF-8")

正解UnsupportedEncodingExceptionです。チェックされた例外を処理するように強制します。Java 1.6以降、バイト配列をに変換するために、別のコンストラクタとして使用できますString

new String(bytes, StandardCharsets.UTF_8)

これは例外をスローしません。

変換後も次のように実行する必要がありますStandardCharsets.UTF_8

"test".getBytes(StandardCharsets.UTF_8)

繰り返しになりますが、チェック済みの例外に対処する必要はありません。


1

私はこの方法でバイト配列を文字列に変換することに成功しました:

public static String byteArrayToString(byte[] data){
    String response = Arrays.toString(data);

    String[] byteValues = response.substring(1, response.length() - 1).split(",");
    byte[] bytes = new byte[byteValues.length];

    for (int i=0, len=bytes.length; i<len; i++) {
        bytes[i] = Byte.parseByte(byteValues[i].trim());
    }

    String str = new String(bytes);
    return str.toLowerCase();
}

1

base64エンコーディングは安全であり、「正しい答え」と主張することはできますが、Javaバイト配列をそのままJava文字列との間で変換する方法を探してここに到着しました。つまり、バイト配列の各メンバーは、対応する文字列にそのまま残り、エンコード/トランスポートに余分なスペースは必要ありません。

8ビットの透過エンコーディングを説明するこの回答は、私にとって非常に役に立ちました。ISO-8859-1テラバイトのバイナリデータを使用して、base64エンコーディングに必要な拡張されたスペース要件なしで正常に変換(バイナリ<->文字列)を行ったので、ユースケース-YMMVで安全です。

これは、いつ、どのような場合に実験すべきかを説明するのにも役立ちました。


0
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;    

private static String base64Encode(byte[] bytes)
{
    return new BASE64Encoder().encode(bytes);
}

private static byte[] base64Decode(String s) throws IOException
{
    return new BASE64Decoder().decodeBuffer(s);
}

どうして?バイトを文字列に変換するためにBase64を通過するのはなぜですか?オーバーヘッド。
james.garriss 2015年

0

ここで作業コード。

            // Encode byte array into string . TemplateBuffer1 is my bytearry variable.

        String finger_buffer = Base64.encodeToString(templateBuffer1, Base64.DEFAULT);
        Log.d(TAG, "Captured biometric device->" + finger_buffer);


        // Decode String into Byte Array. decodedString is my bytearray[] 
        decodedString = Base64.decode(finger_buffer, Base64.DEFAULT);


-1

String使用からバイトを読み取り、バイトデータを文字列に変換するバイトストリームの代わりにCharストリームでByteArrayInputStreamラップしBufferedReaderます。

package com.cs.sajal;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;

public class TestCls {

    public static void main(String[] args) {

        String s=new String("Sajal is  a good boy");

        try
        {
        ByteArrayInputStream bis;
        bis=new ByteArrayInputStream(s.getBytes("UTF-8"));

        BufferedReader br=new BufferedReader(new InputStreamReader(bis));
        System.out.println(br.readLine());

        }
        catch(Exception e)
        {
            e.printStackTrace();
        }

    }
}

出力は次のとおりです。

サハールはいい子です


-1

変換には単純なforループを使用できます。

public void byteArrToString(){
   byte[] b = {'a','b','$'};
   String str = ""; 
   for(int i=0; i<b.length; i++){
       char c = (char) b[i];
       str+=c;
   }
   System.out.println(str);
}


-3

文字列は、char(16ビット符号なし)のコレクションです。したがって、負の数を文字列に変換しようとすると、変換時に失われます。


1
-1:これは不正解です。'byte'はJavaでは署名付きの型ですが、文字セットのエンコードとデコードを行うライブラリコードでは署名なしとして扱われます。
スティーブンC

符号なしの8ビットデータ型を使用することが、言語での使用に適している理由の良い例です。不必要な混乱を回避します; ^)
ヒキガエル2008年

JavaのUTF-16により、Java charが16ビットになると想定する場合は注意してください。最大32ビットに拡張できます
Joe Plante

1
@Toadはい、UTF-16として保存された場合、一部のUnicode文字は2つのコードポイント(32ビット)を使用します。同じことがUTF-8でも起こります。一部の文字は2/3/4コードポイント、つまり16/24/32ビットを使用します。実際、これがまさにUTFの意味です(つまり、UTF!= Unicode)。
CAFxX

1
@Toadは最初のサロゲートを取得します-つまり、キャラクターの最初の「半分」だけです。String.charAtメソッドとCharacterクラスのドキュメントを見てください。
CAFxX

-3
public class byteString {

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        String msg = "Hello";
        byte[] buff = new byte[1024];
        buff = msg.getBytes("UTF-8");
        System.out.println(buff);
        String m = new String(buff);
        System.out.println(m);


    }

}

getBytesの引数としてCharset Encodingを渡す
Shyam Sreenivasan

1
コードに加えて説明を付けて、この回答を具体化することを検討してください。
チャーリーシュリーサー、2015

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