การแปลงไบต์อาร์เรย์เป็น String (Java)


85

ฉันกำลังเขียนเว็บแอปพลิเคชันใน Google App Engine ช่วยให้ผู้ใช้สามารถแก้ไขโค้ด html โดยทั่วไปที่เก็บเป็น.htmlไฟล์ใน blobstore

ฉันใช้ fetchData เพื่อส่งคืนbyte[]อักขระทั้งหมดในไฟล์ ฉันพยายามพิมพ์เป็น html เพื่อให้ผู้ใช้แก้ไขโค้ด html ทุกอย่างใช้งานได้ดี!

นี่คือปัญหาเดียวของฉันตอนนี้:

อาร์เรย์ไบต์มีปัญหาบางอย่างเมื่อแปลงกลับเป็นสตริง คำพูดที่ชาญฉลาดและตัวละครสองตัวกำลังออกมาดูขี้ขลาด (? หรือสัญลักษณ์ภาษาญี่ปุ่นเป็นต้น) โดยเฉพาะอย่างยิ่งมีหลายไบต์ที่ฉันเห็นว่ามีค่าลบซึ่งเป็นสาเหตุของปัญหา

ราคาสมาร์ทจะกลับมาเป็น-108และ-109ในอาร์เรย์ไบต์ เหตุใดจึงเป็นเช่นนี้และฉันจะถอดรหัสไบต์เชิงลบเพื่อแสดงการเข้ารหัสอักขระที่ถูกต้องได้อย่างไร



สวัสดีฉันรู้ว่ามันเป็นโพสต์เก่าจริงๆ แต่ฉันกำลังประสบปัญหาที่คล้ายกัน ฉันกำลังสร้างพร็อกซี man-in-the-middle สำหรับ ssl ปัญหาที่ฉันกำลังเผชิญก็เหมือนกับของคุณ ผมฟังซ็อกเก็ตและได้รับข้อมูลลงแล้วเข้าไปInputStream byte[]ตอนนี้เมื่อฉันพยายามแปลงbyte[]เป็น String (ฉันต้องใช้ตัวตอบสนองสำหรับการโจมตี) ฉันได้รับตัวละครที่ตลกจริงๆที่เต็มไปด้วยคำพูดและเครื่องหมายคำถามที่ชาญฉลาดและอะไรที่ไม่เป็นเช่นนั้น ผมเชื่อว่าปัญหาของคุณเป็นเช่นเดียวกับระเบิดในขณะที่เราทั้งสองจะจัดการกับในhtml byte[]ขอคำแนะนำได้ไหม
Parul S

อย่างไรก็ตามฉันไปที่ขอบเขตเพื่อค้นหาการเข้ารหัสของระบบของฉันโดยใช้ Sytem.properties และพบว่าเป็น "Cp1252" ตอนนี้ฉันใช้String str=new String(buffer, "Cp1252");แต่ไม่มีตัวช่วย
Parul S

คำตอบ:


141

อาร์เรย์ไบต์ประกอบด้วยอักขระในการเข้ารหัสพิเศษ (ที่คุณควรรู้) วิธีการแปลงเป็น String คือ:

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

By The Way - ไบต์ดิบที่ปรากฏอาจปรากฏเป็นทศนิยมเชิงลบเพียงเพราะ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 แม้ว่า - การมีอยู่ของไบต์นั้นจะกำหนดให้เป็น UTF-8 เท่านั้นหากไม่ได้มาตามหลังไบต์ด้วยชุดสองบิตแรก
Nick Johnson

1
@ Josh Andreas อธิบายว่าทำไม - เนื่องจากbyteมีการลงนามประเภทข้อมูลของ Java ค่า 'ลบ' เป็นเพียงไบต์ที่มีชุดไบต์ที่สำคัญที่สุด นอกจากนี้เขายังอธิบายว่าชุดอักขระที่เป็นไปได้มากที่สุดที่คุณควรใช้คือ Windows-1252 คุณควรทราบว่าจะใช้ชุดอักขระใดจากบริบทหรือการประชุมโดยไม่ต้องเดา
Nick Johnson

25

Java 7 ขึ้นไป

นอกจากนี้คุณยังสามารถผ่านการเข้ารหัสที่คุณต้องการไปStringคอนสตรัคเป็นCharsetคงที่จากStandardCharsets วิธีนี้อาจปลอดภัยกว่าการเข้ารหัสเป็น a Stringตามที่แนะนำในคำตอบอื่น ๆ

ตัวอย่างเช่นสำหรับการเข้ารหัส UTF-8

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

1
นี่คือคำตอบซ้ำตั้งแต่ปี 2011 -1
james.garriss

2
@ james.garriss ฉันไม่คิดว่าจะเป็นเช่นนั้นตราบเท่าที่ฉันเพิ่งพูดถึงตัวสร้างใหม่ที่นำมาใช้ใน java 7 ซึ่งอนุญาตให้ส่งการเข้ารหัสเป็นค่าคงที่ซึ่งในความคิดของฉันดีกว่าและปลอดภัยกว่า api ก่อนหน้านี้ ที่กล่าวถึงในคำตอบก่อนหน้านี้ที่การเข้ารหัสถูกส่งผ่านเป็นสตริงถ้าเลย
davnicwil


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เกิดข้อยกเว้น
Raedwald

4

ฉันแนะนำ Arrays.toString(byte_array);

ขึ้นอยู่กับวัตถุประสงค์ของคุณ ตัวอย่างเช่นฉันต้องการบันทึกอาร์เรย์ไบต์เหมือนกับรูปแบบที่คุณเห็นในขณะที่แก้ไขจุดบกพร่องซึ่งเป็นดังนี้: [1, 2, 3]หากคุณต้องการบันทึกค่าเดียวกันโดยไม่ต้องแปลงไบต์เป็นรูปแบบอักขระให้Arrays.toString (byte_array)ทำเช่นนี้ แต่ถ้าคุณต้องการบันทึกอักขระแทนไบต์คุณควรใช้String s = new String(byte_array)ไฟล์. ในกรณีsนี้เท่ากับเท่ากับ[1, 2, 3]ในรูปแบบของอักขระ


คุณสามารถให้ข้อมูลเพิ่มเติมว่าเหตุใดคุณจึงแนะนำสิ่งนี้ (จะแก้ปัญหาได้หรือไม่คุณสามารถพูดได้ว่าทำไมจึงแก้ปัญหาได้) ขอบคุณ!
Dean J

ขึ้นอยู่กับวัตถุประสงค์ของคุณ ตัวอย่างเช่นฉันต้องการบันทึกอาร์เรย์ไบต์เหมือนกับรูปแบบที่คุณเห็นในขณะที่แก้ไขจุดบกพร่องซึ่งเป็นดังนี้: [1, 2, 3] หากคุณต้องการบันทึกค่าเดียวกันทั้งหมดโดยไม่ต้องแปลงไบต์เป็นรูปแบบอักขระ Arrays.toString (byte_array) ทำสิ่งนี้,. แต่ถ้าคุณต้องการบันทึกอักขระแทนไบต์คุณควรใช้ String s = new String (byte_array) ในกรณีนี้ s จะเท่ากับ [1, 2, 3] ในรูปแบบของอักขระ
ถาม

@sas คุณควรเพิ่มข้อมูลนี้ในคำตอบของคุณเอง (โดยการแก้ไข) แทนที่จะเป็นความคิดเห็น โดยทั่วไปใน SO คุณควรจำไว้เสมอว่าความคิดเห็นอาจถูกลบเมื่อใดก็ได้ - ข้อมูลที่สำคัญจริงๆควรอยู่ในคำตอบ
Jeen Broekstra

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.