Cómo convertir una cadena con codificación Unicode a una cadena de letras


82

Tengo una cadena con caracteres Unicode de escape \uXXXX, y quiero convertirla en letras Unicode normales. Por ejemplo:

"\u0048\u0065\u006C\u006C\u006F World"

debe convertirse

"Hello World"

Sé que cuando imprimo la primera cadena ya se muestra Hello world. Mi problema es que leo los nombres de los archivos de un archivo y luego los busco. Los nombres de los archivos en el archivo se escapan con codificación Unicode, y cuando busco los archivos, no puedo encontrarlos, ya que busca un archivo con \uXXXXsu nombre.


¿Estas seguro? ¿No cree que los caracteres simplemente se imprimen cuando Unicode se escapa?
Hot Licks

5
\u0048 es H - son uno y el mismo. Las cadenas en Java están en Unicode.
Hot Licks

Supongo que el problema podría estar en mi api de java a unix; la cadena que obtengo es algo así como \ u3123 \ u3255_file_name.txt. Y Java no lo oculta.
SharonBL

3
UTF-8 es una codificación Unicode.
Pavel Radzivilovsky

5
Esta no es una respuesta a tu pregunta, pero déjame aclarar la diferencia entre Unicode y UTF-8, que muchas personas parecen confundir. Unicode es un particular, uno-a-uno correspondencia entre personajes como los conocemos ( a, b, $, £, etc) a los enteros. Por ejemplo, al símbolo Ase le da el número 65 y \nes 10. Esto no tiene nada que ver con cómo se representan las cadenas o los caracteres en el disco o en un archivo de texto, digamos. UTF-8 es una especificación (es decir, codificación) de cómo estos números enteros (es decir, símbolos) se representan como bytes (cadenas de bits) para que puedan escribirse y leerse sin ambigüedades desde un archivo.
DustByte

Respuestas:


48

Técnicamente haciendo:

String myString = "\u0048\u0065\u006C\u006C\u006F World";

lo convierte automáticamente a "Hello World", por lo que supongo que está leyendo la cadena de algún archivo. Para convertirlo en "Hola" tendrás que analizar el texto en dígitos Unicode separados, (toma el \uXXXXy solo obtén XXXX), luego hazlo Integer.ParseInt(XXXX, 16)para obtener un valor hexadecimal y luego en mayúsculas charpara obtener el carácter real.

Editar: algo de código para lograr esto:

String str = myString.split(" ")[0];
str = str.replace("\\","");
String[] arr = str.split("u");
String text = "";
for(int i = 1; i < arr.length; i++){
    int hexVal = Integer.parseInt(arr[i], 16);
    text += (char)hexVal;
}
// Text will now have Hello

Parece que esa podría ser la solución. ¿Tiene una idea de cómo puedo hacerlo en Java? ¿Puedo hacerlo con String.replaceAll o algo así?
SharonBL

@SharonBL Actualicé con un código, al menos debería darte una idea de por dónde empezar.
NominSim

2
¡Muchas gracias por su ayuda! También encontré otra solución para eso: String s = StringEscapeUtils.unescapeJava ("\\ u20ac \\ n"); hace el trabajo!
SharonBL

2
Intente reinventar los métodos proporcionados por la biblioteca estándar de Java. solo verifique la implementación pura stackoverflow.com/a/39265921/1511077
Evgeny Lebedev

1
Siempre me sorprende cuando una respuesta de " reinventar la rueda " obtiene tantos votos.
Pedro Lobito

92

El Apache Commons Lang StringEscapeUtils.unescapeJava () puede descodificar correctamente.

import org.apache.commons.lang.StringEscapeUtils;

@Test
public void testUnescapeJava() {
    String sJava="\\u0048\\u0065\\u006C\\u006C\\u006F";
    System.out.println("StringEscapeUtils.unescapeJava(sJava):\n" + StringEscapeUtils.unescapeJava(sJava));
}


 output:
 StringEscapeUtils.unescapeJava(sJava):
 Hello

String sJava = "\ u0048 \\ u0065 \ u006C \ u006C \ u006F"; -----> Haga un cambio simple.
Shreyansh Shah

29

Se puede utilizar StringEscapeUtilsa partir de Apache Commons Lang , es decir:

String Title = StringEscapeUtils.unescapeJava("\\u0048\\u0065\\u006C\\u006C\\u006F");


5
después de agregar dependencia en build.gradle: compile 'commons-lang: commons-lang: 2.6' arriba funcionando bien.
Joseph Mekwan

8

Este método simple funcionará en la mayoría de los casos, pero tropezaría con algo como "u005Cu005C" que debería decodificar a la cadena "\ u0048" pero en realidad decodificaría "H" ya que la primera pasada produce "\ u0048" como la cadena de trabajo que luego es procesado nuevamente por el ciclo while.

static final String decode(final String in)
{
    String working = in;
    int index;
    index = working.indexOf("\\u");
    while(index > -1)
    {
        int length = working.length();
        if(index > (length-6))break;
        int numStart = index + 2;
        int numFinish = numStart + 4;
        String substring = working.substring(numStart, numFinish);
        int number = Integer.parseInt(substring,16);
        String stringStart = working.substring(0, index);
        String stringEnd   = working.substring(numFinish);
        working = stringStart + ((char)number) + stringEnd;
        index = working.indexOf("\\u");
    }
    return working;
}

Intente reinventar los métodos proporcionados por la biblioteca estándar de Java. solo verifique la implementación pura stackoverflow.com/a/39265921/1511077
Evgeny Lebedev

1
Gracias @EvgenyLebedev ... la forma de biblioteca estándar se ve bien y presumiblemente ha sido probada a fondo, muy apreciada.
andrew pate

7

Versión más corta:

public static String unescapeJava(String escaped) {
    if(escaped.indexOf("\\u")==-1)
        return escaped;

    String processed="";

    int position=escaped.indexOf("\\u");
    while(position!=-1) {
        if(position!=0)
            processed+=escaped.substring(0,position);
        String token=escaped.substring(position+2,position+6);
        escaped=escaped.substring(position+6);
        processed+=(char)Integer.parseInt(token,16);
        position=escaped.indexOf("\\u");
    }
    processed+=escaped;

    return processed;
}

Intente reinventar los métodos proporcionados por la biblioteca estándar de Java. solo verifique la implementación pura stackoverflow.com/a/39265921/1511077
Evgeny Lebedev

5

StringEscapeUtils de la biblioteca org.apache.commons.lang3 está obsoleto a partir de 3.6.

Entonces puedes usar su nueva biblioteca de texto común en su lugar:

compile 'org.apache.commons:commons-text:1.9'

OR

<dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-text</artifactId>
   <version>1.9</version>
</dependency>

Código de ejemplo:

org.apache.commons.text.StringEscapeUtils.unescapeJava(escapedString);

4

No está del todo claro a partir de su pregunta, pero supongo que dice que tiene un archivo donde cada línea de ese archivo es un nombre de archivo. Y cada nombre de archivo es algo como esto:

\u0048\u0065\u006C\u006C\u006F

En otras palabras, los caracteres en el archivo de nombres de ficheros son \, u, 0, 0, 4, 8y así sucesivamente.

Si es así, se espera lo que está viendo. Java solo traduce \uXXXXsecuencias en cadenas literales en el código fuente (y cuando lee en Propertiesobjetos almacenados ). Cuando se lee el contenido que el archivo que va a tener una cadena formada por los caracteres \, u, 0, 0, 4, 8y así sucesivamente y no la cadena Hello.

Por lo que tendrá que analizar esa cadena para extraer el 0048, 0065, etc piezas y luego convertirlos a chars y hacer una cadena a partir de esas chars y luego pasar esa cadena a la rutina que se abre el archivo.



3

Solo quería contribuir con mi versión, usando expresiones regulares:

private static final String UNICODE_REGEX = "\\\\u([0-9a-f]{4})";
private static final Pattern UNICODE_PATTERN = Pattern.compile(UNICODE_REGEX);
...
String message = "\u0048\u0065\u006C\u006C\u006F World";
Matcher matcher = UNICODE_PATTERN.matcher(message);
StringBuffer decodedMessage = new StringBuffer();
while (matcher.find()) {
  matcher.appendReplacement(
      decodedMessage, String.valueOf((char) Integer.parseInt(matcher.group(1), 16)));
}
matcher.appendTail(decodedMessage);
System.out.println(decodedMessage.toString());

1

tratar

private static final Charset UTF_8 = Charset.forName("UTF-8");
private String forceUtf8Coding(String input) {return new String(input.getBytes(UTF_8), UTF_8))}

1

una forma fácil que conozco usando JsonObject:

try {
    JSONObject json = new JSONObject();
    json.put("string", myString);
    String converted = json.getString("string");

} catch (JSONException e) {
    e.printStackTrace();
}

1

Aquí está mi solución ...

                String decodedName = JwtJson.substring(startOfName, endOfName);

                StringBuilder builtName = new StringBuilder();

                int i = 0;

                while ( i < decodedName.length() )
                {
                    if ( decodedName.substring(i).startsWith("\\u"))
                    {
                        i=i+2;
                        builtName.append(Character.toChars(Integer.parseInt(decodedName.substring(i,i+4), 16)));
                        i=i+4;
                    }
                    else
                    {
                        builtName.append(decodedName.charAt(i));
                        i = i+1;
                    }
                };

intentar reinventar los métodos estándar proporcionados por la biblioteca estándar de Java. solo verifique la implementación pura stackoverflow.com/a/39265921/1511077
Evgeny Lebedev

1

Escribí una solución de rendimiento y a prueba de errores:

public static final String decode(final String in) {
    int p1 = in.indexOf("\\u");
    if (p1 < 0)
        return in;
    StringBuilder sb = new StringBuilder();
    while (true) {
        int p2 = p1 + 6;
        if (p2 > in.length()) {
            sb.append(in.subSequence(p1, in.length()));
            break;
        }
        try {
            int c = Integer.parseInt(in.substring(p1 + 2, p1 + 6), 16);
            sb.append((char) c);
            p1 += 6;
        } catch (Exception e) {
            sb.append(in.subSequence(p1, p1 + 2));
            p1 += 2;
        }
        int p0 = in.indexOf("\\u", p1);
        if (p0 < 0) {
            sb.append(in.subSequence(p1, in.length()));
            break;
        } else {
            sb.append(in.subSequence(p1, p0));
            p1 = p0;
        }
    }
    return sb.toString();
}

1

Rápido

 fun unicodeDecode(unicode: String): String {
        val stringBuffer = StringBuilder()
        var i = 0
        while (i < unicode.length) {
            if (i + 1 < unicode.length)
                if (unicode[i].toString() + unicode[i + 1].toString() == "\\u") {
                    val symbol = unicode.substring(i + 2, i + 6)
                    val c = Integer.parseInt(symbol, 16)
                    stringBuffer.append(c.toChar())
                    i += 5
                } else stringBuffer.append(unicode[i])
            i++
        }
        return stringBuffer.toString()
    }

0

De hecho, escribí una biblioteca de código abierto que contiene algunas utilidades. Uno de ellos es convertir una secuencia Unicode a String y viceversa. Lo encontré muy útil. Aquí está la cita del artículo sobre esta biblioteca sobre el convertidor Unicode:

La clase StringUnicodeEncoderDecoder tiene métodos que pueden convertir una cadena (en cualquier idioma) en una secuencia de caracteres Unicode y viceversa. Por ejemplo, una cadena "Hola mundo" se convertirá en

"\ u0048 \ u0065 \ u006c \ u006c \ u006f \ u0020 \ u0057 \ u006f \ u0072 \ u006c \ u0064"

y puede ser restaurado.

Aquí está el enlace al artículo completo que explica qué utilidades tiene la biblioteca y cómo hacer que la biblioteca la use. Está disponible como artefacto Maven o como fuente de Github. Es muy fácil de usar. Biblioteca Java de código abierto con filtrado de seguimiento de pila, conversor Unicode de análisis de cadenas silenciosas y comparación de versiones


0

Para Java 9+, puede utilizar el nuevo método replaceAll de la clase Matcher .

private static final Pattern UNICODE_PATTERN = Pattern.compile("\\\\u([0-9A-Fa-f]{4})");

public static String unescapeUnicode(String unescaped) {
    return UNICODE_PATTERN.matcher(unescaped).replaceAll(r -> String.valueOf((char) Integer.parseInt(r.group(1), 16)));
}

public static void main(String[] args) {
    String originalMessage = "\\u0048\\u0065\\u006C\\u006C\\u006F World";
    String unescapedMessage = unescapeUnicode(originalMessage);
    System.out.println(unescapedMessage);
}

Creo que la principal ventaja de este enfoque sobre unescapeJava de StringEscapeUtils (además de no usar una biblioteca adicional) es que puede convertir solo los caracteres Unicode (si lo desea), ya que este último convierte todos los caracteres Java escapados (como \ n o \ t ). Si prefiere convertir todos los caracteres de escape, la biblioteca es realmente la mejor opción.


0

@NominSim Puede haber otro carácter, así que debería detectarlo por longitud.

private String forceUtf8Coding(String str) {
    str = str.replace("\\","");
    String[] arr = str.split("u");
    StringBuilder text = new StringBuilder();
    for(int i = 1; i < arr.length; i++){
        String a = arr[i];
        String b = "";
        if (arr[i].length() > 4){
            a = arr[i].substring(0, 4);
            b = arr[i].substring(4);
        }
        int hexVal = Integer.parseInt(a, 16);
        text.append((char) hexVal).append(b);
    }
    return text.toString();
}

0

UnicodeUnescaperfrom org.apache.commons:commons-texttambién es aceptable.

new UnicodeUnescaper().translate("\u0048\u0065\u006C\u006C\u006F World") devoluciones "Hello World"


-1

Una forma alternativa de lograr esto podría ser utilizar chars()Java 9, que se puede utilizar para iterar sobre los caracteres, asegurándose de que cualquier carácter que se asigne a un punto de código sustituto se pase sin interpretar. Esto se puede utilizar como: -

String myString = "\u0048\u0065\u006C\u006C\u006F World";
myString.chars().forEach(a -> System.out.print((char)a));
// would print "Hello World"

-1

Descubrí que muchas de las respuestas no abordaban el tema de los "Caracteres complementarios". Esta es la forma correcta de apoyarlo. Sin bibliotecas de terceros, implementación pura de Java.

http://www.oracle.com/us/technologies/java/supplementary-142654.html

public static String fromUnicode(String unicode) {
    String str = unicode.replace("\\", "");
    String[] arr = str.split("u");
    StringBuffer text = new StringBuffer();
    for (int i = 1; i < arr.length; i++) {
        int hexVal = Integer.parseInt(arr[i], 16);
        text.append(Character.toChars(hexVal));
    }
    return text.toString();
}

public static String toUnicode(String text) {
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < text.length(); i++) {
        int codePoint = text.codePointAt(i);
        // Skip over the second char in a surrogate pair
        if (codePoint > 0xffff) {
            i++;
        }
        String hex = Integer.toHexString(codePoint);
        sb.append("\\u");
        for (int j = 0; j < 4 - hex.length(); j++) {
            sb.append("0");
        }
        sb.append(hex);
    }
    return sb.toString();
}

@Test
public void toUnicode() {
    System.out.println(toUnicode("😊"));
    System.out.println(toUnicode("🥰"));
    System.out.println(toUnicode("Hello World"));
}
// output:
// \u1f60a
// \u1f970
// \u0048\u0065\u006c\u006c\u006f\u0020\u0057\u006f\u0072\u006c\u0064

@Test
public void fromUnicode() {
    System.out.println(fromUnicode("\\u1f60a"));
    System.out.println(fromUnicode("\\u1f970"));
    System.out.println(fromUnicode("\\u0048\\u0065\\u006c\\u006c\\u006f\\u0020\\u0057\\u006f\\u0072\\u006c\\u0064"));
}
// output:
// 😊
// 🥰
// Hello World

No funciona cuando hay caracteres que no son unicode dentro de la cadena, como: href = \ u0022 \ / en \ / blog \ / d-day-protected-europe-its-demon \ u0022 \ u003E \ n
Mohsen Abasi

-1

Solución para Kotlin:

val sourceContent = File("test.txt").readText(Charset.forName("windows-1251"))
val result = String(sourceContent.toByteArray())

Kotlin usa UTF-8 en todas partes como codificación predeterminada.

El método toByteArray()tiene el argumento predeterminado - Charsets.UTF_8.


no es una respuesta sin ejemplos reales de contenido que no se puede "convertir" con el sugerente bytearray-way. puedes proporcionarlo?
Evgeny Lebedev

String(string.toByteArray())literalmente no logra nada.
rustyx

El método @rustyx toByteArray()tiene un argumento predeterminado con Charsets.UTF_8. Luego crea una cadena de bytearray con la codificación requerida. Probé hoy con windows-1251utf-8, funciona. También hice una comparación a nivel de bytes :)
Evgeny Lebedev

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.