Java: Conversione String da e per ByteBuffer e problemi correlati
-
12-09-2019 - |
Domanda
Sto usando Java NIO per le mie connessioni socket, e il mio protocollo è basato su testo, quindi ho bisogno di essere in grado di convertire le stringhe ad ByteBuffers prima di scrivere al SocketChannel, e convertire i ByteBuffers in arrivo di nuovo a stringhe. Attualmente, sto usando questo codice:
public static Charset charset = Charset.forName("UTF-8");
public static CharsetEncoder encoder = charset.newEncoder();
public static CharsetDecoder decoder = charset.newDecoder();
public static ByteBuffer str_to_bb(String msg){
try{
return encoder.encode(CharBuffer.wrap(msg));
}catch(Exception e){e.printStackTrace();}
return null;
}
public static String bb_to_str(ByteBuffer buffer){
String data = "";
try{
int old_position = buffer.position();
data = decoder.decode(buffer).toString();
// reset buffer's position to its original so it is not altered:
buffer.position(old_position);
}catch (Exception e){
e.printStackTrace();
return "";
}
return data;
}
Questo funziona la maggior parte del tempo, ma mi chiedo se questo è il preferito (o semplice) modo di fare ogni direzione di questa conversione, o se c'è un altro modo per provare. Di tanto in tanto, e apparentemente a caso, chiama a encode()
e decode()
lancerà una
eccezione java.lang.IllegalStateException: Current state = FLUSHED, new state = CODING_END
, o simili, anche se sto usando un nuovo ByteBuffer oggetto ogni volta che una conversione è fatta. Ho bisogno di sincronizzare questi metodi? Un modo migliore per la conversione tra stringhe e ByteBuffers? Grazie!
Soluzione
Controlla la CharsetEncoder
e CharsetDecoder
descrizioni API - Si dovrebbe seguire un specifica sequenza di chiamate di metodo per evitare questo problema. Ad esempio, per CharsetEncoder
:
- Ripristina l'encoder tramite il metodo
reset
, a meno che non sia stato utilizzato in precedenza; - Richiamare il metodo
encode
zero o più volte, fino a quando ingresso supplementare può essere disponibile, passandofalse
per l'argomento endOfInput e riempimento del buffer di ingresso e lavando il buffer di uscita tra le chiamate; - Richiamare il metodo
encode
un'ultima volta, passandotrue
per l'argomento endOfInput; e poi - Richiamare il metodo
flush
modo che l'encoder può lavare qualsiasi stato interno al buffer di uscita.
A proposito, questo è lo stesso approccio che sto usando per NIO anche se alcuni dei miei colleghi stanno convertendo ogni char direttamente a un byte con la consapevolezza che sono solo utilizzando ASCII, che posso immaginare è probabilmente più veloce.
Altri suggerimenti
A meno che le cose sono cambiate, si sta meglio con
public static ByteBuffer str_to_bb(String msg, Charset charset){
return ByteBuffer.wrap(msg.getBytes(charset));
}
public static String bb_to_str(ByteBuffer buffer, Charset charset){
byte[] bytes;
if(buffer.hasArray()) {
bytes = buffer.array();
} else {
bytes = new byte[buffer.remaining()];
buffer.get(bytes);
}
return new String(bytes, charset);
}
Di solito buffer.hasArray () sarà o sempre vero o falso sempre a seconda del caso d'uso. In pratica, a meno che non si vuole veramente farlo funzionare in qualsiasi circostanza, è sicuro per ottimizzare via il ramo che non è necessario.
risposta da Adamski è buona e descrive i passi in un'operazione di codifica utilizzando il metodo encode generale (che prende un buffer di byte come uno degli ingressi)
Tuttavia, il metodo in questione (in questa discussione) è una variante di codifica - encode (CharBuffer a) . Questo è un metodo comodo che implementa l'intera operazione di codifica . (Si prega di vedere documenti java riferimento nel P.S.)
Come per la documentazione, Questo metodo non deve pertanto essere invocata se un'operazione di codifica è già in corso (che è ciò che accade nel codice di ZenBlender - utilizzando statica codificatore / decodificatore in un multi filettato ambiente).
Personalmente, mi piace usare convenienza metodi (oltre i metodi più generali di codifica / decodifica) che prendono via il carico eseguendo tutti i passaggi sotto le coperte.
ZenBlender e Adamski hanno già suggerito diverse opzioni di modi per farlo in modo sicuro questo nei loro commenti. elencandoli tutti qui:
- Creare un nuovo oggetto codificatore / decodificatore quando necessario per ciascuna operazione (non efficiente come potrebbe portare a un gran numero di oggetti). O,
- Utilizza un ThreadLocal per evitare di creare nuovo codificatore / decodificatore per ciascuna operazione. O,
- Sincronizzare l'intera operazione di codifica / decodifica (questo potrebbe non essere preferito a meno che sacrificare un po 'di concorrenza è ok per il vostro programma)
P.S.
Documenti java riferimenti:
- Encode (convenienza) Metodo: http://docs.oracle.com/javase/6/docs/api/java/nio/charset/CharsetEncoder.html#encode%28java.nio.CharBuffer%29
- metodo di codifica generale: http://docs.oracle.com/javase/6/docs/api/java/nio/charset/CharsetEncoder.html#encode%28java.nio.CharBuffer,%20java. nio.ByteBuffer,% 20boolean% 29