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.

SharonBL
fuente
¿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
NominSim
fuente
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
Tony
fuente
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");

Pedro Lobito
fuente
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;
}
Andrew Paté
fuente
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;
}
ssuukk
fuente
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);
Bogdan Kobylynskyi
fuente
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.

Mecánica cuántica
fuente
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());
robertokl
fuente
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))}
Hao
fuente
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();
}
Ashkan Ghodrat
fuente
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;
                    }
                };
AndyW58
fuente
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();
}
neoedmund
fuente
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()
    }
Wefyns
fuente
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

Michael Gantman
fuente
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.

Marcelo Barros
fuente
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();
}
Jun.wan
fuente
0

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

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

anton
fuente
-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"
Naman
fuente
-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
lovestackh343
fuente
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.

Evgeny Lebedev
fuente
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
@rustyx aquí hay una esencia para ti - gist.github.com/lebe-dev/31e31a3399c7885e298ed86810504676
Evgeny Lebedev