วิธีการแปลงอาร์เรย์ไบต์เป็นสตริงและในทางกลับกัน?


248

ฉันต้องแปลงอาร์เรย์เป็นสตริงใน 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
เหตุใดคุณจึงพยายามแปลงข้อมูลไบนารีโดยพลการให้เป็น String ในตอนแรก นอกเหนือจากปัญหาที่ชุดอักขระทั้งหมดคำตอบที่กล่าวถึงแล้วยังมีความจริงที่ว่าคุณกำลังใช้ String ในทางที่ผิดถ้าคุณทำเช่นนี้ เกิดอะไรขึ้นกับการใช้byte[]ข้อมูลไบนารีและStringข้อความของคุณ
Joachim Sauer

8
@Joachim - บางครั้งคุณมีเครื่องมือภายนอกที่สามารถทำสิ่งต่างๆเช่นสตริงการจัดเก็บ คุณต้องการที่จะสามารถเปลี่ยนอาร์เรย์ไบต์เป็นสตริง (เข้ารหัสในบางวิธี) ในกรณีที่
James Moore

คำตอบ:


377

อาร์เรย์ไบต์ของคุณต้องมีการเข้ารหัส การเข้ารหัสไม่สามารถเป็น ASCII ได้หากคุณมีค่าลบ เมื่อคุณทราบแล้วคุณสามารถแปลงชุดไบต์เป็นสตริงโดยใช้:

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

มีพวงของการเข้ารหัสคุณสามารถใช้เป็นดูที่ชั้นรหัสตัวอักษรในjavadocs อาทิตย์


4
@MauricePerry คุณช่วยอธิบายได้ไหมว่าทำไมมันถึงไม่ทำงานUTF-8?
ราวกับว่า Mushtaq

12
@UnKnown เนื่องจาก UTF-8 เข้ารหัสอักขระบางตัวเป็นสตริง 2- หรือ 3- ไบต์ ไม่ใช่ทุกอาร์เรย์ไบต์เป็นสตริงที่เข้ารหัส UTF-8 ที่ถูกต้อง ISO-8859-1 น่าจะเป็นตัวเลือกที่ดีกว่า: อักขระแต่ละตัวจะถูกเข้ารหัสเป็นไบต์
Maurice Perry

1
สิ่งนี้อาจใช้งานได้ แต่คุณควรหลีกเลี่ยงการใช้ String Constructor ด้วยค่าใช้จ่ายทั้งหมด
hfontanez

เพื่อแมหนึ่งไบต์ถึงหนึ่งถ่าน (กับ 8859-1) และไม่มีการจัดการข้อยกเว้น (กับ nio.charset):String str = new String(bytes, java.nio.charset.StandardCharsets.ISO_8859_1);
Iman

1
ตั้งแต่ Java 1.7 คุณสามารถใช้ String ใหม่ (ไบต์, StandardCharsets.UTF_8)
ihebiheb

101

"การแปลงที่เหมาะสม" ระหว่างbyte[]และStringจะระบุการเข้ารหัสที่คุณต้องการใช้อย่างชัดเจน หากคุณเริ่มต้นด้วย a byte[]และในความเป็นจริงไม่มีข้อมูลตัวอักษรไม่มี "การแปลงที่เหมาะสม" Strings สำหรับข้อความbyte[]เป็นข้อมูลไบนารีและสิ่งเดียวที่สมเหตุสมผลจริงๆที่ต้องทำคือหลีกเลี่ยงการแปลงระหว่างพวกเขาเว้นแต่คุณจะต้องทำอย่างแน่นอน

หากคุณต้องใช้Stringเพื่อเก็บข้อมูลไบนารีวิธีที่ปลอดภัยที่สุดคือการใช้การเข้ารหัสBase64


1
ใช่การเข้ารหัสอักขระเป็นสิ่งที่คุณต้องรู้เกี่ยวกับการแปลงระหว่างสตริงและไบต์
Raedwald

4
Base64 และคุณช่วยชีวิตฉันไว้
59

2
การเข้ารหัส Base64 แก้ปัญหาของฉันได้ UTF-8 ไม่สามารถใช้งานได้กับอินพุตทั้งหมด
Al-Alamin

37

ปัญหารากคือ (ฉันคิดว่า) ที่คุณไม่ได้ตั้งใจใช้ชุดอักขระที่:

 bytes != encode(decode(bytes))

ในบางกรณี. UTF-8 เป็นตัวอย่างของชุดอักขระดังกล่าว โดยเฉพาะอย่างยิ่งลำดับของไบต์ที่ไม่ถูกต้องในการเข้ารหัส UTF-8 หากตัวถอดรหัส UTF-8 พบหนึ่งในลำดับเหล่านี้จะต้องทิ้งไบต์ที่ละเมิดหรือถอดรหัสเป็น Unicode codepoint สำหรับ "no character ดังกล่าว" โดยปกติเมื่อคุณพยายามเข้ารหัสอักขระเป็นไบต์ผลลัพธ์จะแตกต่างกัน

ทางออกคือ:

  1. ชัดเจนเกี่ยวกับการเข้ารหัสอักขระที่คุณใช้ เช่นใช้ตัวสร้างสตริงและString.toByteArrayวิธีการที่มีชุดอักขระที่ชัดเจน
  2. ใช้ชุดอักขระที่ถูกต้องสำหรับข้อมูลไบต์ของคุณ ... หรืออีกอันหนึ่ง (เช่น "Latin-1" โดยที่ลำดับไบต์ทั้งหมดแมปกับอักขระ Unicode ที่ถูกต้อง
  3. หากไบต์ของคุณคือ (จริง ๆ ) ข้อมูลไบนารีและคุณต้องการที่จะสามารถส่ง / รับพวกเขาผ่านช่องทาง "ข้อความตาม" ใช้สิ่งที่ต้องการการเข้ารหัส Base64 ... ซึ่งถูกออกแบบมาเพื่อวัตถุประสงค์นี้

1
ขอบคุณสำหรับเคล็ดลับการใช้การเข้ารหัส "Latin-1"!
Gonzo

31

เราเพียงแค่ต้องสร้างใหม่Stringด้วยอาร์เรย์: http://www.mkyong.com/java/how-do-convert-byte-array-to-string-in-java/

String s = new String(bytes);

ไบต์ของสตริงผลลัพธ์จะแตกต่างกันไปขึ้นอยู่กับชุดอักขระที่คุณใช้ สตริงใหม่ (ไบต์) และสตริงใหม่ (ไบต์ Charset.forName ("utf-8")) และสตริงใหม่ (ไบต์ 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
ที่ทำให้เข้าใจผิด chars (และดังนั้นข้อความที่แสดง) ของผลStringแตกต่างเมื่อถอดรหัสbytesที่แตกต่างกัน การแปลงกลับเป็นไบต์โดยใช้การเข้ารหัสเริ่มต้น (ใช้String#getBytes("charset")เพื่อระบุเป็นอย่างอื่น) จะจำเป็นต้องแตกต่างกันเนื่องจากจะแปลงอินพุตที่แตกต่างกัน สตริงไม่ได้จัดเก็บข้อมูลที่byte[]ทำจากพวกเขาcharไม่มีการเข้ารหัสและ a Stringจะไม่เก็บไว้เป็นอย่างอื่น
zapl

14

การใช้new String(byOriginal)และการแปลงกลับเป็นการbyte[]ใช้getBytes()ไม่รับประกันว่าทั้งสองจะbyte[]มีค่าเท่ากัน เพราะนี่คือการเรียกร้องให้StringCoding.encode(..)ซึ่งจะเข้ารหัสเพื่อString Charset.defaultCharset()ในระหว่างการเข้ารหัสนี้ผู้เข้ารหัสอาจเลือกที่จะแทนที่ตัวละครที่ไม่รู้จักและทำการเปลี่ยนแปลงอื่น ๆ ดังนั้นการใช้String.getBytes()อาจไม่คืนค่าอาร์เรย์เท่าเดิมเมื่อคุณส่งผ่านไปยังตัวสร้าง


9

ทำไมมีปัญหา:ตามที่มีคนระบุไว้แล้ว: ถ้าคุณเริ่มต้นด้วยไบต์ [] และในความเป็นจริงมันไม่ได้มีข้อมูลข้อความไม่มี "การแปลงที่เหมาะสม" สตริงมีไว้สำหรับข้อความไบต์ [] ใช้สำหรับข้อมูลไบนารีและสิ่งเดียวที่สมเหตุสมผลจริงๆที่ต้องทำคือหลีกเลี่ยงการแปลงระหว่างพวกเขาเว้นแต่คุณจะต้องทำอย่างแน่นอน

ฉันกำลังสังเกตปัญหานี้เมื่อฉันพยายามสร้าง byte [] จากไฟล์ pdf แล้วแปลงเป็น String จากนั้นให้ String เป็นอินพุตและแปลงกลับเป็นไฟล์

ดังนั้นตรวจสอบให้แน่ใจว่าตรรกะการเข้ารหัสและถอดรหัสของคุณเหมือนกับที่ฉันทำ ฉันเข้ารหัสไบต์ [] ไปที่ Base64 อย่างชัดเจนและถอดรหัสเพื่อสร้างไฟล์อีกครั้ง

กรณีการใช้งาน: เนื่องจากข้อ จำกัด บางอย่างที่ฉันได้พยายามที่จะที่ส่งbyte[]ในrequest(POST)และกระบวนการที่เป็นดังนี้:

ไฟล์ PDF >> Base64.encodeBase64 (ไบต์ []) >> สตริง >> ส่งคำขอ (POST) >> รับสตริง >> Base64.decodeBase64 (ไบต์ []) >> สร้างไบนารี

ลองใช้วิธีนี้และใช้งานได้กับฉัน ..

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

การแปลงจากไบต์ [] เป็นสตริง:

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

1
สิ่งนี้ไม่ตอบคำถาม
james.garriss

ไม่ตอบคำถาม แต่มีประโยชน์ +1
Lazy Ninja

5

ฉันสังเกตเห็นบางสิ่งที่ไม่ได้อยู่ในคำตอบใด ๆ คุณสามารถแปลงแต่ละไบต์ในอาร์เรย์ไบต์เป็นอักขระและวางไว้ในอาร์เรย์ถ่าน งั้นสตริงก็คือ

new String(cbuf)
โดยที่ cbuf เป็นอาร์เรย์ถ่าน ในการแปลงกลับให้วนไปตามสตริงที่ใช้ในการคัดเลือกอักขระแต่ละตัวเป็นไบต์เพื่อใส่ลงในอาร์เรย์ไบต์และอาร์เรย์ไบต์นี้จะเหมือนกับครั้งแรก


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

นี่เป็นวิธีการสองสามอย่างที่แปลงอาร์เรย์ของไบต์เป็นสตริง ฉันได้ทดสอบพวกเขาพวกเขาทำงานได้ดี

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 byte เป็น / จาก Java String ตามที่เป็นอยู่ นั่นคือที่สมาชิกแต่ละคนของอาร์เรย์ไบต์ยังคงอยู่ในสตริงเดียวกันโดยไม่ต้องมีพื้นที่เพิ่มเติมสำหรับการเข้ารหัส / การขนส่ง

คำตอบนี้อธิบายการเข้ารหัสแบบโปร่งใส 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

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ใช้ByteArrayInputStreamและล้อมด้วยBufferedReaderซึ่งเป็น Char Stream แทน Byte Stream ซึ่งแปลงข้อมูลไบต์เป็น String

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

    }
}

ผลลัพธ์คือ:

Sajal เป็นเด็กดี


-1

คุณสามารถใช้การวนรอบแบบง่ายสำหรับการแปลงได้:

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's (16 บิตที่ไม่ได้ลงชื่อ) ดังนั้นหากคุณจะแปลงจำนวนลบเป็นสตริงพวกเขาจะหายไปในการแปล


1
-1: นี่ไม่ถูกต้อง ในขณะที่ 'byte' เป็นประเภทที่เซ็นชื่อใน Java พวกเขาจะถือว่าเป็นไม่ได้ลงนามโดยรหัสห้องสมุดที่ทำการเข้ารหัสชุดอักขระและถอดรหัส
สตีเฟ่นซี

ตัวอย่างที่ดีว่าทำไมการมีประเภทข้อมูล 8 บิตที่ไม่ได้ลงนามจริงๆเป็นความคิดที่ดีที่จะมีในภาษา หลีกเลี่ยงความสับสนที่ไม่จำเป็น; ^)
คางคก

ระมัดระวังเกี่ยวกับการสมมติว่าถ่าน Java จะเป็น 16 บิตเพราะของ Java UTF-16 ที่พวกเขาสามารถขยายได้ถึง 32 บิต
โจ Plante

1
@ โหลดจริงใช่อักขระ Unicode บางตัวเมื่อจัดเก็บในรูปแบบ UTF-16 ใช้จุดโค้ดสองจุดคือ 32 บิต สิ่งนี้เกิดขึ้นใน UTF-8: ตัวละครบางตัวใช้จุดโค้ดสอง / สาม / สี่ตัวนั่นคือ 16/24/32 บิต ในความเป็นจริงนั่นคือสิ่งที่เกี่ยวกับ UTF (เช่น UTF! = Unicode)
CAFxX

1
@ โหลดคุณจะได้รับตัวแทนครั้งแรก - นั่นคือ "ครึ่งแรก" ของตัวละครเท่านั้น ดูเอกสารสำหรับวิธีการString.charAtและคลาสตัวละคร
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);


    }

}

ผ่านการเข้ารหัส Charset เป็นการโต้แย้งเพื่อรับ Bytes
Shyam Sreenivasan

1
คุณอาจต้องการพิจารณาคำตอบนี้เพิ่มเติมด้วยคำอธิบายเพิ่มเติมจากรหัส
Charlie Schliesser

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.