Java ByteBuffer เป็น String


122

นี่เป็นแนวทางที่ถูกต้องในการแปลง 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

6
ใช่ฉันทำและได้ผล แต่ฉันได้เห็นการใช้งานอื่น ๆ ที่ซับซ้อนกว่าเช่นstackoverflow.com/questions/1252468/…
vikky.rk

1
@ Doorknob et. อัล เขาขาดการเข้ารหัสและตัวอย่างของเขา (เมื่อแก้ไขไวยากรณ์) จะใช้งานได้ แต่วิธีการของเขายังไม่ถูกต้อง
กัส

คำตอบ:


83

EDIT (2018):คำตอบของพี่น้องที่แก้ไขโดย @xinyongCheng เป็นแนวทางที่ง่ายกว่าและควรเป็นคำตอบที่ยอมรับได้

แนวทางของคุณจะสมเหตุสมผลหากคุณรู้ว่าไบต์อยู่ในชุดอักขระเริ่มต้นของแพลตฟอร์ม ในตัวอย่างของคุณนี่เป็นจริงเนื่องจากk.getBytes()ส่งคืนไบต์ในชุดอักขระเริ่มต้นของแพลตฟอร์ม

บ่อยขึ้นคุณจะต้องระบุการเข้ารหัส อย่างไรก็ตามมีวิธีที่ง่ายกว่าคำถามที่คุณเชื่อมโยง String API จัดเตรียมเมธอดที่แปลงระหว่างสตริงและอาร์เรย์ไบต์ [] ในการเข้ารหัสเฉพาะ วิธีการเหล่านี้แนะนำให้ใช้ CharsetEncoder / CharsetDecoder "เมื่อต้องควบคุมกระบวนการถอดรหัส [เข้ารหัส] มากขึ้น"

ในการรับไบต์จาก String ในการเข้ารหัสเฉพาะคุณสามารถใช้วิธี getBytes () แบบพี่น้อง:

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

ในการใส่ไบต์ที่มีการเข้ารหัสเฉพาะลงใน String คุณสามารถใช้ตัวสร้าง String อื่น:

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 - คุณมีไบต์ [] ByteBuffer.get(byte[] dst, int offset, int length)คุณเบื่อที่จะ คุณสามารถสร้าง String ออกมาได้ด้วยตัวสร้าง String () `String (byte [] bytes, int offset, int length, Charset charset) คุณสามารถใช้ค่าออฟเซ็ตและค่าความยาวเดียวกันสำหรับทั้งสองสาย
Andy Thomas

ไม่มีเมธอด k.getBytes () ใน java.nio ByteBuffer (อาจไม่มีในเวอร์ชันที่ฉันใช้) ดังนั้นฉันจึงใช้วิธี k.array () ซึ่งจะคืนค่าไบต์ []
Madura Pradeep

@MaduraPradeep - ในโค้ดตัวอย่างในคำถามและคำตอบkนี้เป็น String ไม่ใช่ ByteBuffer
Andy Thomas

โปรดทราบว่า UTF-8 อาจไม่ใช่ชุดอักขระที่เหมาะสมที่สุดสำหรับการแปลงไบต์เป็นสตริงและในทางกลับกัน สำหรับการแมปไบต์แบบ 1 ต่อ 1 กับตัวอักษรให้ดีขึ้นให้ใช้ ISO-8859-1 ดูstackoverflow.com/questions/9098022/…
asmaier

103

มีวิธีการที่ง่ายกว่าในการถอดรหัสByteBuffera Stringโดยไม่มีปัญหาใด ๆ กล่าวถึงโดย Andy Thomas

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

2
โปรดทราบว่า UTF-8 อาจไม่ใช่ชุดอักขระที่เหมาะสมที่สุดสำหรับการแปลงไบต์เป็นสตริงและในทางกลับกัน สำหรับการทำแผนที่ 1 ต่อ 1 ไบต์จะดีกว่าตัวอักษรใช้ ISO-8859-1 ดูstackoverflow.com/questions/9098022/...
asmaier

นอกจากนี้คุณไม่จำเป็นต้องใช้สตริงจริงๆCharBuffer decode()ผลตอบแทนคือCharSequence(like String) ดังนั้นคุณสามารถหลีกเลี่ยงสำเนาเพิ่มเติมและใช้งานได้โดยตรง
David Ehrmann

15

ลองสิ่งนี้:

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

NB คุณไม่สามารถแปลงอาร์เรย์ไบต์เป็นสตริงได้อย่างถูกต้องโดยไม่ทราบการเข้ารหัส

ฉันหวังว่านี่จะช่วยได้


10
UTF-8 น่าจะเป็นการเดาเริ่มต้นที่ดีกว่า ASCII?
กัส

3
ไม่ควรระบุเนื่องจาก OP ใช้ k.getBytes () ซึ่งใช้ชุดอักขระเริ่มต้นของแพลตฟอร์ม
Andy Thomas

7
ไม่ใช่ทุกบัฟเฟอร์ที่สำรองโดยอาร์เรย์ดังนั้น.array()อาจมีข้อยกเว้น
Dzmitry Lazerka

สัตว์เลี้ยงลูกด้วยนมบางคนไม่สนับสนุน.array()วิธีนี้
ScalaWilliam

3
ระวัง! หากคุณใช้array()คุณต้องใช้arrayOffset()เพื่อเริ่มต้นในตำแหน่งที่ถูกต้องในอาร์เรย์ด้วย! นี่เป็นข้อผิดพลาดเล็กน้อยเนื่องจากโดยปกติ arrayOffset () คือ 0; แต่ในบางกรณีที่เกิดขึ้นได้ยากซึ่งคุณจะไม่ได้รับข้อบกพร่องที่หาได้ยากหากคุณไม่คำนึงถึง
oliver

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 () จะเป็นจริงหรือเท็จขึ้นอยู่กับกรณีการใช้งานของคุณ ในทางปฏิบัติเว้นแต่คุณต้องการให้มันทำงานได้จริงไม่ว่าในสถานการณ์ใดก็ตามคุณสามารถเพิ่มประสิทธิภาพสาขาที่คุณไม่ต้องการได้อย่างปลอดภัย แต่คำตอบที่เหลืออาจใช้ไม่ได้กับ ByteBuffer ที่สร้างผ่าน ByteBuffer.allocateDirect ()


หากบัฟเฟอร์ถูกสร้างผ่านByteBuffer.wrap(bytes, offset, size)โรงงาน.array()จะส่งคืนbytesอาร์เรย์ทั้งหมด ใช้แบบฟอร์ม xinyong Cheng ที่แนะนำดีกว่า
Lev Kuznetsov

.decode () บน Charset เป็นทางออกที่ดีกว่าตามที่ตกลงกัน ฉันรู้สึกว่าบริบทของคำตอบของฉันเป็นข้อมูลที่มีประโยชน์ แต่ตอนนี้น้อยกว่ามาก
Fuwjax

2
ระวัง! หากคุณใช้array()คุณต้องใช้arrayOffset()เพื่อเริ่มต้นในตำแหน่งที่ถูกต้องในอาร์เรย์ด้วย! นี่เป็นข้อผิดพลาดเล็กน้อยเนื่องจากโดยปกติ arrayOffset () คือ 0; แต่ในบางกรณีที่เกิดขึ้นได้ยากซึ่งคุณจะไม่ได้รับข้อบกพร่องที่หาได้ยากหากคุณไม่คำนึงถึง
oliver

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


2

รากของคำถามนี้คือการถอดรหัสไบต์เป็นสตริงได้อย่างไร?

ซึ่งสามารถทำได้ด้วย 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());
  • ก่อนอื่นเราสร้างช่องและอ่านในบัฟเฟอร์
  • จากนั้นวิธีการถอดรหัสจะถอดรหัสบัฟเฟอร์ Latin1 เป็นถ่านบัฟเฟอร์
  • จากนั้นเราสามารถใส่ผลลัพธ์ลงในสตริงได้

รหัสของคุณไม่ได้ถอดรหัสจาก latin1 ถึง utf8 แม้ว่ารหัสของคุณจะถูกต้อง แต่การเรียกใช้ CharBuffer utf8Buffer นั้นค่อนข้างทำให้เข้าใจผิดเนื่องจากไม่มีการเข้ารหัส
Björn Lindqvist

1

ขอให้สังเกต (นอกเหนือจากปัญหาการเข้ารหัส) ว่าโค้ดที่ซับซ้อนกว่านั้นบางส่วนที่เชื่อมโยงจะทำให้เกิดปัญหาในการรับส่วน "ใช้งาน" ของ ByteBuffer ที่เป็นปัญหา (เช่นโดยใช้ตำแหน่งและขีด จำกัด ) แทนที่จะเข้ารหัสไบต์ทั้งหมด ในอาร์เรย์สำรองทั้งหมด (ดังตัวอย่างในคำตอบเหล่านี้)


1

แปลง String เป็น ByteBuffer จากนั้นจาก ByteBuffer กลับเป็น String โดยใช้ Java:

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 ที่ส่งไปยังอาร์เรย์ ():

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:

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

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

0

นี่คือฟังก์ชั่นง่ายๆสำหรับการแปลงบัฟเฟอร์ไบต์เป็นสตริง:

public String byteBufferToString(ByteBuffer bufferData) {
    byte[] buffer = new byte[bufferData.readableByteCount()];
    // read bufferData and insert into buffer 
    data.read(buffer);
    // CharsetUtil supports UTF_16, ASCII, and many more
    String text = new String(buffer, CharsetUtil.UTF_8);
    System.out.println("Text: "+text);
    return text;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.