2009-07-28 13 views
13

Dato un array di byte che è una stringa codificata UTF-8 o dati binari arbitrari, quali approcci possono essere utilizzati in Java per determinare qual è?Come posso verificare se una matrice di byte contiene una stringa Unicode in Java?

La matrice può essere generato dal codice simile a:

byte[] utf8 = "Hello World".getBytes("UTF-8"); 

alternativa potrebbe essere stato generato dal codice simile a:

byte[] messageContent = new byte[256]; 
for (int i = 0; i < messageContent.length; i++) { 
    messageContent[i] = (byte) i; 
} 

Il punto chiave è che non sappiamo cosa la matrice contiene ma è necessario scoprirlo per riempire la seguente funzione:

public final String getString(final byte[] dataToProcess) { 
    // Determine whether dataToProcess contains arbitrary data or a UTF-8 encoded string 
    // If dataToProcess contains arbitrary data then we will BASE64 encode it and return. 
    // If dataToProcess contains an encoded string then we will decode it and return. 
} 

Come sarebbe esteso anche a UTF-16 o altri meccanismi di codifica?

+1

Una domanda simile ha alcuni link utili da Edward Wilde - http://stackoverflow.com/questions/377294/howto-identify-utf- 8-stringhe codificate – JonoW

risposta

-1

Provare a decodificarlo. Se non si verificano errori, allora è una stringa UTF-8 valida.

+2

-1: errore reale. È possibile che un flusso binario non testuale sia decodificato come una stringa UTF-8 valida. Se la decodifica UTF-8 fallisce, ciò implica che i tuoi dati binari non sono UTF-8; ma se la decodifica UTF-8 non fallisce, ciò non garantisce che i dati binari siano UTF-8. –

+1

+1 assolutamente corretto. Se decodifica senza errori, sono dati testuali UTF-8 validi. Potrebbero essere dati testuali che non hanno assolutamente senso, come un mix selvaggio di caratteri latini, cinesi, tailandesi e greci, ma questa è una distinzione semantica, non tecnica. –

+1

Fair point Michael. Immagino che in quel caso avrei dovuto dire: -1 Non rispondere alla domanda. Asserire che si tratta di una stringa UTF-8 valida non risponde alla domanda, che stava cercando di scoprire se si trattava di una stringa o di dati binari. Solo perché è una rappresentazione UTF-8 valida non ti dice molto se i dati originali sono binari (che per coincidenza è solo UTF-8 valido per coincidenza) o se l'originale era un vero e proprio dato testuale. –

10

Non è possibile prendere quella decisione, con tutta esattezza in tutti i casi, a causa di una codifica UTF-8 stringa è un tipo di dati binari arbitrari, ma si può cercare sequenze di byte che sono invalid in UTF-8. Se ne trovi uno, sai che non è UTF-8.

Se la matrice è sufficientemente grande, questo dovrebbe funzionare bene poiché è molto probabile che tali sequenze appaiano in dati binari "casuali" quali dati compressi o file di immagine.

Tuttavia, è possibile ottenere dati UTF-8 validi che decodificano una stringa di caratteri totalmente priva di senso (probabilmente da tutti i tipi di script diversi). Questo è più probabile con brevi sequenze. Se sei preoccupato per questo, potresti dover fare un'analisi più approfondita per vedere se i caratteri che sono lettere appartengono tutti allo stesso code chart. Poi di nuovo, questo potrebbe produrre falsi negativi quando hai un input di testo valido che mescola gli script.

0

Se l'array di byte inizia con Byte Order Mark (BOM), sarà facile distinguere quale codifica è stata utilizzata. Le classi Java standard per l'elaborazione di flussi di testo probabilmente gestiranno questo automaticamente per te.

Se non si dispone di una distinta base nei dati di byte, ciò sarà sostanzialmente più difficile. Le classi .NET possono eseguire analisi statistiche per provare a elaborare la codifica, ma penso che questo presupponga che tu sappia che tu si occupano di dati di testo (semplicemente non so quale codifica è stata utilizzata).

Se si dispone del controllo del formato per i dati di input, la scelta migliore è assicurarsi che contenga un segno di ordine byte.

+1

Java non inserisce automaticamente una BOM e non la rimuove in decodifica. – McDowell

+1

Erk, dovrei dire che Java non gestisce le BOM per UTF-8. Se lo fa o no per UTF-16/UTF-32 dipende dal meccanismo di codifica scelto: http://java.sun.com/javase/6/docs/technotes/guides/intl/encoding.doc.html – McDowell

3

La domanda presuppone che esista una differenza fondamentale tra una stringa e dati binari. Anche se questo è intuitivo, è quasi impossibile definire con precisione qual è questa differenza.

Una stringa Java è una sequenza di quantità di 16 bit che corrisponde a uno dei (quasi) 2 ** 16 punti di codifica di base Unicode. Ma se guardi quei "caratteri" a 16 bit, ognuno potrebbe rappresentare ugualmente un numero intero, una coppia di byte, un pixel e così via. I bit pattern non hanno nulla di intrinseco in ciò che dice ciò che rappresentano.

Supponiamo ora di riformulare la domanda come richiesta di un modo per distinguere il testo codificato UTF-8 da dati binari arbitrari. questo aiuta? In teoria no, perché i pattern di bit che codificano qualsiasi testo scritto possono anche essere una sequenza di numeri. (.? E 'difficile dire che cosa "arbitrario" significa in realtà qui si può dirmi come verificare se un numero è "arbitrario")

Il meglio che possiamo fare qui è la seguente:

  1. Verifica se i byte sono una codifica UTF-8 valida.
  2. Verificare se le quantità decodificate a 16 bit sono tutti punti di riferimento UTF-8 legali, "assegnati". (Alcune quantità di 16 bit sono illegali (ad esempio 0xffff) e altre non sono attualmente assegnate a corrispondere a nessun carattere.) Ma cosa succede se un documento di testo utilizza davvero un codice punto non assegnato?
  3. Verificare se i codepoint Unicode appartengono ai "piani" previsti in base alla lingua presunta del documento. Ma cosa succede se non sai che lingua aspettarti o se un documento che utilizza più lingue?
  4. Test è la sequenza di codepoint che assomigliano a parole, frasi o altro. Ma se avessimo dei "dati binari" che includono sequenze di testo incorporate?

In sintesi, si può dire che una sequenza di byte non è sicuramente UTF-8 se la decodifica fallisce. Oltre a ciò, se si fanno ipotesi sul linguaggio, si può dire che una sequenza di byte è probabilmente o probabilmente non un documento di testo con codifica UTF-8.

IMO, la cosa migliore che puoi fare è evitare di entrare in una situazione in cui il tuo programma ha bisogno di prendere questa decisione. E se non puoi evitarlo, riconosci che il tuo programma potrebbe sbagliare. Con il pensiero e il duro lavoro, puoi renderlo improbabile, ma la probabilità non sarà mai pari a zero.

4

Ecco un modo per utilizzare la codifica UTF-8 regex "binario" dal W3C site

static boolean looksLikeUTF8(byte[] utf8) throws UnsupportedEncodingException 
{ 
    Pattern p = Pattern.compile("\\A(\n" + 
    " [\\x09\\x0A\\x0D\\x20-\\x7E]    # ASCII\\n" + 
    "| [\\xC2-\\xDF][\\x80-\\xBF]    # non-overlong 2-byte\n" + 
    "| \\xE0[\\xA0-\\xBF][\\x80-\\xBF]   # excluding overlongs\n" + 
    "| [\\xE1-\\xEC\\xEE\\xEF][\\x80-\\xBF]{2} # straight 3-byte\n" + 
    "| \\xED[\\x80-\\x9F][\\x80-\\xBF]   # excluding surrogates\n" + 
    "| \\xF0[\\x90-\\xBF][\\x80-\\xBF]{2}  # planes 1-3\n" + 
    "| [\\xF1-\\xF3][\\x80-\\xBF]{3}   # planes 4-15\n" + 
    "| \\xF4[\\x80-\\x8F][\\x80-\\xBF]{2}  # plane 16\n" + 
    ")*\\z", Pattern.COMMENTS); 

    String phonyString = new String(utf8, "ISO-8859-1"); 
    return p.matcher(phonyString).matches(); 
} 

Come originariamente scritta, l'espressione regolare è pensato per essere utilizzato su un array di byte, ma non si può fare con regex di Java; l'obiettivo deve essere qualcosa che implementa l'interfaccia CharSequence (quindi anche uno char[] è fuori). Decodificando byte[] come ISO-8859-1, si crea una stringa in cui ogni char ha lo stesso valore numerico senza segno del byte corrispondente nell'array originale.

Come altri hanno fatto notare, i test come questo può solo dirvi la byte[]potrebbe contengono testo UTF-8, non che fa. Ma la regex è così esauriente, sembra estremamente improbabile che dati binari grezzi possano scivolare oltre. Anche un array di tutti gli zeri non corrisponde, dal momento che la regex non corrisponde mai a NUL. Se le uniche possibilità sono UTF-8 e binario, sarei disposto a fidarmi di questo test.

E mentre ci sei, puoi estrarre il BOM UTF-8 se ce n'è uno; altrimenti, il CharsetDecoder UTF-8 lo passerà come se fosse un testo.

UTF-16 sarebbe molto più difficile, perché ci sono pochissime sequenze di byte che sono sempre non valide. Gli unici a cui riesco a pensare a mano a mano sono personaggi di alto valore surrogato a cui mancano i loro compagni a basso surrogato, o viceversa.Oltre a ciò, è necessario un certo contesto per decidere se una determinata sequenza è valida. Potresti avere una lettera cirillica seguita da un ideogramma cinese seguita da un dingbat con faccine sorridenti, ma sarebbe perfettamente valida UTF-16.

-1

Penso che Michael l'abbia spiegato bene in his answer questo potrebbe essere l'unico modo per scoprire se una matrice di byte contiene tutte le sequenze utf-8 valide. Sto usando seguente codice in PHP

function is_utf8($string) { 

    return preg_match('%^(?: 
      [\x09\x0A\x0D\x20-\x7E]   # ASCII 
     | [\xC2-\xDF][\x80-\xBF]    # non-overlong 2-byte 
     | \xE0[\xA0-\xBF][\x80-\xBF]  # excluding overlongs 
     | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte 
     | \xED[\x80-\x9F][\x80-\xBF]  # excluding surrogates 
     | \xF0[\x90-\xBF][\x80-\xBF]{2}  # planes 1-3 
     | [\xF1-\xF3][\x80-\xBF]{3}   # planes 4-15 
     | \xF4[\x80-\x8F][\x80-\xBF]{2}  # plane 16 
    )*$%xs', $string); 

} 

preso da W3.org

+0

"cosa gli approcci possono essere usati in Java " –

0

Nella domanda iniziale: come posso verificare se un array di byte contiene una stringa Unicode in Java ?; Ho scoperto che il termine Java Unicode si riferisce essenzialmente alle unità di codice Utf16. Ho affrontato personalmente questo problema e ho creato un codice che potesse aiutare chiunque con questo tipo di domande a trovare qualche risposta.

Ho creato 2 metodi principali, uno visualizzerà Unità Codice Utf-8 e l'altro creerà Unità Codice Utf-16. Utf-16 Code Units è ciò che incontrerai con Java e JavaScript ... comunemente visto nel formato "\ ud83d"

Per ulteriori informazioni sulle unità di codice e la conversione, provare il sito Web;

https://r12a.github.io/apps/conversion/

Ecco il codice ...

byte[] array_bytes = text.toString().getBytes(); 
    char[] array_chars = text.toString().toCharArray(); 
    System.out.println(); 
    byteArrayToUtf8CodeUnits(array_bytes); 
    System.out.println(); 
    charArrayToUtf16CodeUnits(array_chars); 


public static void byteArrayToUtf8CodeUnits(byte[] byte_array) 
{ 
    /*for (int k = 0; k < array.length; k++) 
    { 
     System.out.println(name + "[" + k + "] = " + "0x" + byteToHex(array[k])); 
    }*/ 
    System.out.println("array.length: = " + byte_array.length); 
    //------------------------------------------------------------------------------------------ 
    for (int k = 0; k < byte_array.length; k++) 
    { 
     System.out.println("array byte: " + "[" + k + "]" + " converted to hex" + " = " + byteToHex(byte_array[k])); 
    } 
    //------------------------------------------------------------------------------------------ 
} 
public static void charArrayToUtf16CodeUnits(char[] char_array) 
{ 
    /*Utf16 code units are also known as Java Unicode*/ 
    System.out.println("array.length: = " + char_array.length); 
    //------------------------------------------------------------------------------------------ 
    for (int i = 0; i < char_array.length; i++) 
    { 
     System.out.println("array char: " + "[" + i + "]" + " converted to hex" + " = " + charToHex(char_array[i])); 
    } 
    //------------------------------------------------------------------------------------------ 
} 
static public String byteToHex(byte b) 
{ 
    //Returns hex String representation of byte b 
    char hexDigit[] = 
      { 
        '0', '1', '2', '3', '4', '5', '6', '7', 
        '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' 
      }; 
    char[] array = { hexDigit[(b >> 4) & 0x0f], hexDigit[b & 0x0f] }; 
    return new String(array); 
} 
static public String charToHex(char c) 
{ 
    //Returns hex String representation of char c 
    byte hi = (byte) (c >>> 8); 
    byte lo = (byte) (c & 0xff); 

    return byteToHex(hi) + byteToHex(lo); 
} 
Problemi correlati