2009-07-28 18 views
13

Dada una matriz de bytes que es una cadena UTF-8 codificados o datos binarios arbitrarios, lo que se aproxima se puede utilizar en Java para determinar qué es?¿Cómo puedo verificar si una matriz de bytes contiene una cadena Unicode en Java?

La matriz puede ser generada por un código similar a:

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

Alternativamente, puede haber sido generada por un código similar a:

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

El punto clave es que no sabemos lo la matriz contiene pero necesita averiguar para completar la siguiente función:

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. 
} 

¿Cómo se extendería esto también para cubrir UTF-16 u otros mecanismos de codificación?

+1

Una pregunta similar tiene algunos enlaces útiles de Eduardo Wilde - http://stackoverflow.com/questions/377294/howto-identify-utf- 8-encoded-strings – JonoW

Respuesta

-1

Intente decodificarlo. Si no obtiene ningún error, entonces es una cadena UTF-8 válida.

+2

-1: error de hecho. Es posible decodificar una secuencia binaria no textual como una cadena UTF-8 válida. Si la decodificación UTF-8 falla, eso implica que sus datos binarios no son UTF-8; pero si la decodificación UTF-8 _ no falla_, no garantiza _ que los datos binarios _es_ UTF-8. –

+1

+1 Absolutamente correcto. Si se decodifica sin error, es información textual UTF-8 válida. Pueden ser datos textuales que no tienen ningún sentido, como una mezcla salvaje de caracteres latinos, chinos, tailandeses y griegos, pero esa es una distinción semántica, no técnica. –

+1

Fair point Michael. Supongo que en ese caso debería haber dicho: -1 No respondiendo la pregunta. Afirmar que es una cadena UTF-8 válida no responde a la pregunta, que estaba tratando de averiguar si era una cadena o datos binarios. El hecho de que sea una representación UTF-8 válida no dice mucho acerca de si los datos originales son binarios (lo que resulta ser un UTF-8 válido por coincidencia) o si el original era información textual genuina. –

10

No es posible tomar esa decisión con plena exactitud en todos los casos, debido a que una cadena UTF-8 codificado es un tipo de datos binarios arbitrarios, pero se puede buscar secuencias de bytes que son invalid in UTF-8. Si encuentra alguno, sabe que no es UTF-8.

Si matriz es lo suficientemente grande, esto debería funcionar bien, ya que es muy probable que para tales secuencias aparezcan en datos binarios "al azar", tales como datos comprimidos o archivos de imagen.

Sin embargo, es posible obtener datos válidos UTF-8 que decodifica a una cadena totalmente sin sentido de caracteres (probablemente de todo tipo de secuencias de comandos diferentes). Esto es más probable con secuencias cortas. Si le preocupa eso, puede que tenga que hacer un análisis más detallado para ver si los caracteres que son letras pertenecen todos al mismo code chart. Por otra parte, esto puede producir falsos negativos cuando tiene una entrada de texto válida que mezcla guiones.

0

Si la matriz de bytes comienza con Byte Order Mark (BOM), entonces será fácil distinguir qué codificación se ha utilizado. Las clases estándar de Java para procesar secuencias de texto probablemente tratarán esto automáticamente.

Si usted no tiene una lista de materiales en su byte de datos que será mucho más difícil - clases .NET pueden realizar análisis estadístico para tratar de resolver la codificación, pero creo que esto es en el supuesto de que usted sabe que usted se trata de datos de texto (simplemente no se sabe qué codificación se utilizó).

Si usted tiene ningún control sobre el formato de los datos de entrada su mejor opción sería la de asegurarse de que contiene un byte marca de orden.

+1

Java no inserta una lista de materiales automáticamente y no la eliminará en la decodificación. – McDowell

+1

Erk, debería decir que Java no maneja las listas de materiales para UTF-8. Si lo hace o no para UTF-16/UTF-32 depende del mecanismo de codificación elegido: http://java.sun.com/javase/6/docs/technotes/guides/intl/encoding.doc.html – McDowell

3

La pregunta asume que hay una diferencia fundamental entre una cadena y datos binarios. Si bien esto es intuitivamente, es casi imposible definir con precisión cuál es esa diferencia.

A Java cadena es una secuencia de 16 bits cantidades que corresponden a una de las (casi) 2 ** 16 Unicode puntos de código básicas. Pero si observa esos "caracteres" de 16 bits, cada uno podría representar igualmente un número entero, un par de bytes, un píxel, etc. Los patrones de bits no tienen nada intrínseco que diga lo que representan.

Supongamos ahora que reformuló su pregunta como pidiendo una forma de distinguir el TEXTO codificado en UTF-8 de los datos binarios arbitrarios. ¿Esto ayuda? En teoría no, porque los patrones de bits que codifican cualquier texto escrito también pueden ser una secuencia de números. (.? Es difícil decir cuál es "arbitraria" realmente significa aquí ¿Me puede decir cómo comprobar si un número es "arbitraria")

Lo mejor que podemos hacer aquí es la siguiente:

  1. Pruebe si los bytes son una codificación UTF-8 válida.
  2. Pruebe si las cantidades descodificadas de 16 bits son legales, puntos de código "asignados" UTF-8. (Algunas cantidades de 16 bits son ilegales (por ejemplo, 0xffff) y otras no están asignadas actualmente para corresponder a ningún carácter). ¿Pero qué pasa si un documento de texto realmente usa un punto de código no asignado?
  3. Pruebe si los puntos de código Unicode pertenecen a los "planos" que espera basándose en el lenguaje asumido del documento. Pero, ¿qué sucede si no sabe qué idioma esperar o si es un documento que utiliza varios idiomas?
  4. La prueba es que las secuencias de los puntos de código se parecen a palabras, oraciones, o lo que sea. Pero, ¿qué pasaría si tuviéramos algunos "datos binarios" que incluyesen secuencias de texto incrustadas?

En resumen, se puede decir que una secuencia de bytes definitivamente no es UTF-8 si falla la decodificación. Más allá de eso, si hace suposiciones sobre el lenguaje, puede decir que una secuencia de bytes es probablemente o probablemente no un documento de texto codificado UTF-8.

IMO, lo mejor que puede hacer es evitar entrar en una situación donde el programa necesita tomar esta decisión. Y si no puede evitarlo, reconozca que su programa puede equivocarse. Con el pensamiento y el trabajo duro, puede hacer que sea poco probable, pero la probabilidad nunca será cero.

4

Aquí está una manera de utilizar la codificación UTF-8 expresión regular "binario" de la 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(); 
} 

Como originalmente escrita, la expresión regular está destinado a ser utilizado en una matriz de bytes, pero no se puede hacer eso con las expresiones regulares de Java; el objetivo tiene que ser algo que implemente la interfaz CharSequence (de modo que también está fuera char[]). Al decodificar el byte[] como ISO-8859-1, crea una cadena en la que cada char tiene el mismo valor numérico sin signo que el byte correspondiente en la matriz original.

Como otros han señalado, las pruebas de este tipo pueden sólo le dirá la byte[]podía contienen texto UTF-8, no es que lo hace . Pero la expresión regular es tan exhaustiva, parece extremadamente improbable que los datos binarios en bruto puedan pasarla. Incluso una matriz de todos los ceros no coincidiría, ya que la expresión regular nunca coincide con NUL. Si las únicas posibilidades son UTF-8 y binarias, estaría dispuesto a confiar en esta prueba.

Y mientras lo hace, podría despojar a la BOM UTF-8 si hay una; de lo contrario, el UTF-8 CharsetDecoder lo pasará como si fuera texto.

UTF-16 sería mucho más difícil, porque hay muy pocas secuencias de bytes que son siempre inválidas. Los únicos que puedo pensar de forma directa son los personajes de alto sustituto a los que les faltan compañeros de bajo rango, o viceversa.Más allá de eso, necesitarías un contexto para decidir si una secuencia dada es válida. Puede tener una letra cirílica seguida de un ideograma chino seguido de un dingbat de cara sonriente, pero sería perfectamente válido UTF-16.

-1

Creo que Michael lo ha explicado bien in his answer esta puede ser la única forma de averiguar si una matriz de bytes contiene todas las secuencias válidas de utf-8. Estoy usando siguiente código en 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); 

} 

tomado de W3.org

+0

"what los enfoques se pueden usar en Java " –

0

En la pregunta original: ¿Cómo puedo comprobar si una matriz de bytes contiene una cadena Unicode en Java ?; Descubrí que el término Java Unicode se refiere esencialmente a unidades de código Utf16. Hice este problema yo mismo y creé un código que podría ayudar a cualquiera con este tipo de preguntas a encontrar algunas respuestas.

He creado 2 métodos principales, uno mostrará Utf-8 Code Units y el otro creará Utf-16 Code Units. UTF-16 Código Unidades es lo que se encontrará con Java y JavaScript ... comúnmente visto en la forma "\ ud83d"

Para obtener más ayuda con las Unidades de Conversión de códigos y probar el sitio web;

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

Aquí está el código ...

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