Dizeye Java ByteBuffer


122

Bu, ByteBuffer'ı String'e bu şekilde dönüştürmek için doğru bir yaklaşım mı?

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

Sormamın nedeni, bunun çok basit görünmesi, oysa Java gibi diğer yaklaşımlar : String'i ByteBuffer'a ve ByteBuffer'dan dönüştürme ve ilgili sorunlar daha karmaşık görünüyor.


3
Peki denedin mi?
tckmn

6
Evet yaptım ve işe yarıyor. Ancak stackoverflow.com/questions/1252468/…
vikky.rk

1
@Doorknob et. ark. Eksik kodlaması ve örneği (sözdizimi düzeltildiğinde) işe yarayacak, ancak yöntemi hala doğru değil.
Gus

Yanıtlar:


83

DÜZENLEME (2018): @ xinyongCheng tarafından düzenlenen kardeş cevabı daha basit bir yaklaşımdır ve kabul edilen cevap olmalıdır.

Baytların platformun varsayılan karakter kümesinde olduğunu bilseydiniz yaklaşımınız mantıklı olacaktır. Örneğinizde bu doğrudur çünkük.getBytes() baytları platformun varsayılan karakter kümesinde döndürür.

Daha sık olarak, kodlamayı belirtmek isteyeceksiniz. Ancak, bunu yapmanın bağladığınız sorudan daha basit bir yolu var. String API, belirli bir kodlamada bir String ile bir bayt [] dizisi arasında dönüşüm sağlayan yöntemler sağlar. Bu yöntemler, "kod çözme [kodlama] işlemi üzerinde daha fazla kontrol gerektiğinde " CharsetEncoder / CharsetDecoder kullanılmasını önerir .

Belirli bir kodlamada bir String'den bayt almak için kardeş getBytes () yöntemini kullanabilirsiniz:

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

Bir String'e belirli bir kodlamaya sahip baytlar koymak için farklı bir String yapıcısı kullanabilirsiniz:

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

Bunun ByteBuffer.array()isteğe bağlı bir işlem olduğunu unutmayın . ByteBuffer'ınızı bir dizi ile oluşturduysanız, bu diziyi doğrudan kullanabilirsiniz. Aksi takdirde, güvende olmak istiyorsanız, ByteBuffer.get(byte[] dst, int offset, int length)baytları tampondan bir bayt dizisine almak için kullanın .


ve ByteBuffer.getfonksiyonda, girdi yine bir bayt dizisidir, bunu nasıl elde edebilirim? Tekrar k.getbytes demek mantıklı değil, değil mi?
William Kinaan

@WilliamKinaan - Beslediğiniz bayta [] sahipsiniz ByteBuffer.get(byte[] dst, int offset, int length). String () yapıcısı `String (byte [] bytes, int offset, int length, Charset charset) ile bir String oluşturabilirsiniz. Her iki arama için aynı ofset ve uzunluk değerlerini kullanabilirsiniz.
Andy Thomas

Java.nio.ByteBuffer'da k.getBytes () yöntemi yoktur (kullandığım sürümde olmayabilir). Bu yüzden bayt [] döndürecek k.array () yöntemini kullandım.
Madura Pradeep

@MaduraPradeep - Sorudaki örnek kodda ve bu cevap, kByteBuffer değil, String'dir.
Andy Thomas

UTF-8'in baytları dizelere dönüştürmek için en uygun karakter kümesi olmayabileceğini ve bunun tersini unutmayın.
Baytların

103

Andy Thomas'ın bahsettiği gibi, a'yı sorunsuz bir ByteBufferşekilde çözmek için daha basit bir yaklaşım var String.

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

2
UTF-8'in baytları dizelere dönüştürmek için en uygun karakter kümesi olmayabileceğini ve bunun tersini unutmayın. Baytların karakterlere 1'e 1 eşlenmesi için ISO-8859-1'i daha iyi kullanın, bkz. Stackoverflow.com/questions/9098022/… .
asmaier

Ayrıca, gerçekten bir dizgeye ihtiyacınız yoktur , CharBuffer decode()dönüşler bir CharSequence(gibidir String), böylece fazladan bir kopyadan kaçınabilir ve onu doğrudan kullanabilirsiniz.
David Ehrmann

15

Bunu dene:

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

NB. bir bayt dizisini kodlamasını bilmeden String'e doğru şekilde dönüştüremezsiniz.

Umarım bu yardımcı olur


10
UTF-8 muhtemelen ASCII'den daha iyi bir varsayılan tahmindir?
Gus

3
OP'nin platformun varsayılan karakter kümesini kullanan k.getBytes () 'i kullandığı düşünüldüğünde, hiçbiri belirtilmemelidir.
Andy Thomas

7
Tüm tamponlar bir dizi tarafından desteklenmez, bu nedenle .array()bir istisna oluşturabilir.
Dzmitry Lazerka

Tüm bytebuffers .array()yöntemi desteklemez .
ScalaWilliam

3
Dikkatli! Eğer kullanırsanız array(), siz gerekir ayrıca kullanmak arrayOffset()dizide doğru pozisyonda başlayın! Bu ince bir tuzaktır, çünkü genellikle arrayOffset () 0'dır; ancak böyle olmadığı nadir durumlarda, hesaba katmazsanız bulmanız zor hatalar elde edersiniz.
oliver

13

Sadece belirtmek istedim, ByteBuffer.array () 'ın her zaman çalışacağını varsaymak güvenli değildir.

byte[] bytes;
if(buffer.hasArray()) {
    bytes = buffer.array();
} else {
    bytes = new byte[buffer.remaining()];
    buffer.get(bytes);
}
String v = new String(bytes, charset);

Genellikle buffer.hasArray (), kullanım durumunuza bağlı olarak her zaman doğru veya yanlış olacaktır. Pratikte, herhangi bir koşulda çalışmasını gerçekten istemediğiniz sürece, ihtiyacınız olmayan şubeyi optimize etmek güvenlidir. Ancak yanıtların geri kalanı, ByteBuffer.allocateDirect () aracılığıyla oluşturulan bir ByteBuffer ile çalışmayabilir.


Tampon ByteBuffer.wrap(bytes, offset, size)fabrika aracılığıyla oluşturulursa .array()tüm bytesdiziyi döndürür . Xinyong Cheng'in önerdiği formu daha iyi kullanın
Lev Kuznetsov

Charset üzerindeki .decode () daha iyi bir çözüm olduğu kabul edildi. Cevabımın bağlamının yararlı bilgi olduğunu hissediyorum, ancak şimdi çok daha az.
Fuwjax

2
Dikkatli! Eğer kullanırsanız array(), siz gerekir ayrıca kullanmak arrayOffset()dizide doğru pozisyonda başlayın! Bu ince bir tuzaktır, çünkü genellikle arrayOffset () 0'dır; ancak böyle olmadığı nadir durumlarda, hesaba katmazsanız bulmanız zor hatalar elde edersiniz.
oliver

8

Basitçe çağırmaya atıfta bulunan cevaplar array()tam olarak doğru değil: tampon kısmen tüketildiğinde veya bir dizinin bir parçasına atıfta bulunduğunda ( ByteBuffer.wrapbelirli bir ofsette bir dizi yapabilirsiniz , mutlaka baştan değil), hesaba katmalıyız bizim hesaplamalarımızda. Bu, her durumda tamponlar için çalışan genel çözümdür (kodlamayı kapsamaz):

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

Kodlamayla ilgili endişeler için Andy Thomas'ın cevabına bakın.


2

Bu sorunun kökü, baytların dizgeye nasıl çözüleceğidir?

bu, JAVA NIO CharSet ile yapılabilir:

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());
  • Önce bir kanal oluşturup bir tamponda okuyoruz
  • Daha sonra decode yöntemi, bir Latin1 tamponunun kodunu char tamponuna çözer
  • Daha sonra sonucu örneğin bir String'e koyabiliriz

Kodunuz latin1'den utf8'e kod çözmüyor. Kodunuz doğru olsa da, CharBuffer utf8Buffer'ı çağırmak biraz yanıltıcıdır çünkü kodlaması yoktur.
Björn Lindqvist

1

Bağlanan daha karmaşık kodların bir kısmının, tüm baytları kodlamaktan ziyade, söz konusu ByteBuffer'ın "etkin" bölümünü (örneğin konum ve sınır kullanarak) alma zahmetine yol açtığına (kodlama sorununun yanı sıra) dikkat edin. destek dizisinin tamamında (bu yanıtlardaki örneklerin çoğunun yaptığı gibi).


1

Bir String'i ByteBuffer'a, ardından ByteBuffer'dan Java kullanarak String'e dönüştürün:

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

İlk olarak yazdırılan çıplak dizeyi ve ardından ByteBuffer'ı array () 'e dönüştüren:

obufscate thdé alphebat and yolo!!
obufscate thdé alphebat and yolo!!

Ayrıca bu benim için yardımcı oldu, dizeyi ilkel baytlara düşürmek neler olup bittiğini incelemeye yardımcı olabilir:

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

Dizenizi UTF-8 olarak yorumlanmış ve ardından tekrar ISO-8859-1 olarak yazdırır:

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

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

Bir bayt tamponunu dizeye dönüştürmek için basit bir işlev:

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;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.