2013-07-08 17 views
6

Sto cercando di trovare un metodo di sottostringa, o un metodo characterAt che funzioni su stringhe contenenti testo codificato UTF-8 in JAVA.Sottostringa o carattere Metodo per stringhe UTF8 con 2+ byte in JAVA

Internamente, JAVA funziona con UTF-16. Ciò significa che una stringa è composta da caratteri con una dimensione di 2 byte. Un carattere UTF-8 può avere una dimensione massima di 6 byte. Quando JAVA lo memorizza all'interno di una stringa, divide il carattere UTF-8 su più caratteri.

Ad esempio: Il carattere U + 20000 (UTF-8 Hex: F0 A0 80 80) è memorizzato internamente in JAVA come stringa con due caratteri (UTF-16 Hex: D840 e DC00).

Quando si dispone di una stringa contenente un carattere UTF-8 a 4 byte e si utilizza la lunghezza, la risposta è "2". Quando si utilizza la sottostringa (0,1), si ottiene la prima metà del carattere.

Alcuni codice per illustrare questo:

ByteBuffer inputBuffer = ByteBuffer.wrap(new byte[]{(byte)0xF0, (byte)0xA0, (byte)0x80, (byte)0x80}); 
    CharBuffer data = Charset.forName("UTF-8").decode(inputBuffer); 
    String string_test = data.toString(); 
    int length = string_test.length(); 
    String first_half = string_test.substring(0, 1); 
    String second_half = string_test.substring(1, 2); 
    String full_character = string_test.substring(0, 2); 

Tutto questo, anche se inattesa, non è un bug, dal momento che Java funziona in UTF-16. Il supporto per UTF-8 intrinseco sarebbe bello. Ma non è lì.

JAVA ha una classe nella libreria predefinita o esiste una classe da qualche parte che fornisce il supporto UTF-8? Come in:

  • utf8string.length() - restituisce 1 se v'è un carattere 4 byte
    ci
  • utf8string.getCharacterAt (0) - restituisce il primo carattere, non la prima metà .
  • utf8string.substring (0,1) - restituisce il primo carattere, non la prima metà.

Oppure, qual è la soluzione comunemente utilizzata per questo? Convertire tutti i caratteri UTF-8 non UTF-16 supportati in un carattere UTF-16 predefinito durante la lettura di file UTF-8? E, di conseguenza, perdere tutte le informazioni sui personaggi compresi nel range di codici che UTF-16 non supporta? Questo non è necessariamente un problema nella mia specifica implementazione, quindi se c'è un modo comune per farlo, sarei interessato.

risposta

7

JAVA ha una classe nella libreria predefinita oppure esiste una classe da qualche parte che fornisce il supporto UTF-8?

Non si sta veramente seguendo il supporto UTF-8. Stai cercando punti di codice Unicode (semplici numeri interi a 32 bit) anziché unità di codice UTF-16. E sì, Java fornisce il supporto per questo, ma non è enormemente facile da lavorare.

Ad esempio, per ottenere un particolare punto di codice, utilizzare String.codePointAt - tenendo presente che l'indice fornito sia in termini di UTF-16 unità di codice, non punti di codice.

Per trovare la lunghezza nei punti di codice, utilizzare String.codePointCount.

Per trovare una sottostringa, è necessario trovare l'offset in termini di unità di codice UTF-16, quindi utilizzare il metodo normale substring; utilizzare String.offsetByCodePoints per trovare l'indice corretto.

Fondamentalmente controllare l'API String con tutti i metodi che contengono codePoint.

+0

Grazie, ha risposto alla prima parte della mia domanda. Per la seconda parte, ho utilizzato http://stackoverflow.com/questions/12867000/how-to-remove-surrogate-characters-in-java. Dal momento che non volevo avere personaggi in quei codepoint che complicano le operazioni con le stringhe. – Wouter

+0

Inoltre, per altre persone che potrebbero avere bisogno di tutti i punti di codice, potrebbe essere interessante dare un'occhiata a: http://avro.apache.org/docs/1.6.1/api/java/org/apache/avro /util/Utf8.html – Wouter

+0

Quindi, è per la sottostringa? public static String substringUtf8 (String utf8String, int from, int to) { return utf8String.substring (utf8String.offsetByCodePoints (0, from), utf8String.offsetByCodePoints (0, to))} – RobertG

0

Quello che dovresti cercare è il supporto nativo di Java per UTF-32. Scopri i metodi String#*codePoint*, ad esempio codePointAt.