2009-06-22 22 views
32

¿Cuál es la forma "correcta" de comparar un punto de código con un carácter de Java? Por ejemplo:Comparando un char con un punto de código?

int codepoint = String.codePointAt(0); 
char token = '\n'; 

Sé que probablemente lo que puedo hacer:

if (codepoint==(int) token) 
{ ... } 

pero este código parece frágil. ¿Hay un método API formal para comparar codepoints a chars, o convertir el char hasta un codepoint para comparar?

Respuesta

40

Un poco de fondo: cuando Java apareció en 1995, el tipo char se basaba en la especificación original "Unicode 88", que estaba limitada a 16 bits. Un año después, cuando se implementó Unicode 2.0, se introdujo el concepto de caracteres sustitutos para ir más allá del límite de 16 bits.

Java internamente representa todos String s en formato UTF-16. Para los puntos de código que exceden U + FFFF, el punto de código está representado por un par sustituto, es decir, dos char s; el primero es la unidad de código de subrogación alta (en el rango \ uD800- \ uDBFF), el segundo es el bajo unidad de código sustituto (en el rango \ uDC00- \ uDFFF).

Desde los primeros días, todos los métodos básicos de Character se basaban en la suposición de que un punto de código se podía representar en un char, de modo que así son las firmas de método. Supongo que para preservar la compatibilidad con versiones anteriores que no se modificó cuando llegó Unicode 2.0 y se necesita precaución al tratar con ellos. Para citar de Java documentation:

  • Los métodos que solo aceptan un valor de char no pueden admitir caracteres suplementarios. Tratan los valores de char de los rangos de sustitución como caracteres indefinidos. Por ejemplo, Character.isLetter ('\ uD840') devuelve false, aunque este valor específico si es seguido por cualquier valor de bajo sustituto en una cadena representaría una letra.
  • Los métodos que aceptan un valor int admiten todos los caracteres Unicode, incluidos los caracteres suplementarios. Por ejemplo, Character.isLetter (0x2F81A) devuelve verdadero porque el valor del punto de código representa una letra (un ideograma CJK).

Lanzamiento de la char a un int, como lo hace en su muestra, aunque funciona bien.

+0

http://java.sun.com/developer/technicalArticles/Intl/Supplementary/ explica las decisiones de diseño detrás de los puntos de código en Java. – Gili

2

Para los caracteres en el plano multilingüe básico, al convertir el carácter en un int obtendrá el punto de código. Esto corresponde a todos los valores Unicode que se pueden codificar en un solo valor de char de 16 bits. Los valores fuera de este plano (con puntos de código que exceden 0xffff) no se pueden expresar como un solo carácter. Esta es probablemente la razón por la cual no hay Character.toCodePoint (valor de char).

0

Java utiliza un modelo de 16 bits (UTF-16) para la manipulación de caracteres, por lo que cualquier caracteres con puntos de código> 0xFFFF se almacenan en las cuerdas como pares de caracteres de 16 bits utilizando dos surrogate caracteres para representar el plano y personaje dentro del plano.

Si desea manejar caracteres y cadenas correctamente de acuerdo con el estándar Unicode completo, debe procesar cadenas teniendo esto en cuenta.

XML se preocupa mucho por esto; es útil acceder a la clase XMLChar en Xerces (que viene con Java versión 5.0 y superior) para código relacionado con caracteres.

También es instructivo observar el procesador XSLT/XQuery Saxon, ya que es una aplicación XML de buen comportamiento, tiene que tener en cuenta cómo Java almacena los puntos de código en cadenas. XQuery 1.0 y XPath 2.0 tienen funciones para codepoints-to-string y string-to-codepoints; podría ser instructivo obtener una copia de Saxon y jugar con ellos para ver cómo funcionan.

3

Para un carácter que puede representarse con un único carácter (16 bits, plano multilingüe básico), puede obtener el código simplemente al convertir el carácter en un número entero (como sugiere la pregunta), por lo que no es necesario método especial para realizar una conversión.

Si está comparando un char con un punto de código, no necesita ninguna carcasa especial. Simplemente compare el char con el int directamente (como lo sugiere la pregunta). Si el int representa un punto de código fuera del plano multilingüe básico, el resultado siempre será falso.

9

La clase Character contiene muchos métodos útiles para trabajar con puntos de código Unicode. Tenga en cuenta métodos como Character.toChars(int) que devuelven una matriz de caracteres. Si su punto de código se encuentra en el rango suplementario, entonces la matriz tendrá dos caracteres de longitud.

Cómo quiere comparar los valores depende de si desea admitir la gama completa de valores Unicode. Este código de ejemplo se puede usar para iterar a través de los puntos de código de una cadena, probando para ver si hay una coincidencia para el carácter suplementario MATEMATICO & # x005F; FRAKTUR & # x005F; CAPITAL & # x005F; G (& # x1D50A; - U + 1D50A):

public final class CodePointIterator { 

    private final String sequence; 
    private int index = 0; 

    public CodePointIterator(String sequence) { 
    this.sequence = sequence; 
    } 

    public boolean hasNext() { 
    return index < sequence.length(); 
    } 

    public int next() { 
    int codePoint = sequence.codePointAt(index); 
    index += Character.charCount(codePoint); 
    return codePoint; 
    } 

    public static void main(String[] args) { 
    String sample = "A" + "\uD835\uDD0A" + "B" + "C"; 
    int match = 0x1D50A; 
    CodePointIterator pointIterator = new CodePointIterator(sample); 
    while (pointIterator.hasNext()) { 
     System.out.println(match == pointIterator.next()); 
    } 
    } 
} 

para Java 8 en adelante CharSequence.codePoints() se pueden utilizar:

public static void main(String[] args) { 
    String sample = "A" + "\uD835\uDD0A" + "B" + "C"; 
    int match = 0x1D50A; 
    sample.codePoints() 
     .forEach(cp -> System.out.println(cp == match)); 
} 

he creado un table para ayudar a conseguir una manija en la longitud de cuerda y la comparación de los casos Unicode que a veces necesitan ser manejadas.

+1

El cuerpo de 'next()' podría escribirse como 'int codePoint = sequence.codePointAt (index); index + = Character.charCount (codePoint); return codePoint; 'que podría leer mejor y ser un poco más eficiente. –

+0

Para concatenar los caracteres en una cadena, StringBuffer.appendCodePoint (int codePoint). – IceArdor

Cuestiones relacionadas