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 \uXXXX
su nombre.
\u0048
esH
- son uno y el mismo. Las cadenas en Java están en Unicode.a
,b
,$
,£
, etc) a los enteros. Por ejemplo, al símboloA
se le da el número 65 y\n
es 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.Respuestas:
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\uXXXX
y solo obténXXXX
), luego hazloInteger.ParseInt(XXXX, 16)
para obtener un valor hexadecimal y luego en mayúsculaschar
para 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
fuente
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
fuente
Se puede utilizar
StringEscapeUtils
a partir de Apache Commons Lang , es decir:String Title = StringEscapeUtils.unescapeJava("\\u0048\\u0065\\u006C\\u006C\\u006F");
fuente
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; }
fuente
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; }
fuente
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:
fuente
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:
En otras palabras, los caracteres en el archivo de nombres de ficheros son
\
,u
,0
,0
,4
,8
y así sucesivamente.Si es así, se espera lo que está viendo. Java solo traduce
\uXXXX
secuencias en cadenas literales en el código fuente (y cuando lee enProperties
objetos almacenados ). Cuando se lee el contenido que el archivo que va a tener una cadena formada por los caracteres\
,u
,0
,0
,4
,8
y así sucesivamente y no la cadenaHello
.Por lo que tendrá que analizar esa cadena para extraer el
0048
,0065
, etc piezas y luego convertirlos achar
s y hacer una cadena a partir de esaschar
s y luego pasar esa cadena a la rutina que se abre el archivo.fuente
Actualizaciones con respecto a las respuestas que sugieren el uso de The Apache Commons Lang: StringEscapeUtils.unescapeJava () - estaba en desuso,
La sustitución es Apache Commons Texto 's StringEscapeUtils.unescapeJava ()
fuente
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());
fuente
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))}
fuente
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(); }
fuente
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; } };
fuente
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(); }
fuente
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() }
fuente
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:
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
fuente
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.
fuente
@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(); }
fuente
UnicodeUnescaper
fromorg.apache.commons:commons-text
también es aceptable.new UnicodeUnescaper().translate("\u0048\u0065\u006C\u006C\u006F World")
devoluciones"Hello World"
fuente
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"
fuente
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
fuente
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
.fuente
String(string.toByteArray())
literalmente no logra nada.toByteArray()
tiene un argumento predeterminado conCharsets.UTF_8
. Luego crea una cadena de bytearray con la codificación requerida. Probé hoy conwindows-1251
utf-8, funciona. También hice una comparación a nivel de bytes :)