Mam ciąg znaków ze znakami ucieczki Unicode\uXXXX
i chcę go przekonwertować na zwykłe litery Unicode. Na przykład:
"\u0048\u0065\u006C\u006C\u006F World"
powinno stać się
"Hello World"
Wiem, że kiedy drukuję pierwszy napis, to już się wyświetla Hello world
. Mój problem polega na tym, że czytam nazwy plików z pliku, a następnie ich szukam. Nazwy plików w pliku są chronione kodowaniem Unicode, a kiedy szukam plików, nie mogę ich znaleźć, ponieważ szuka pliku z \uXXXX
w nazwie.
\u0048
jestH
- są jednym i tym samym. Łańcuchy w Javie są w Unicode.a
,b
,$
,£
, etc) do liczb całkowitych. Np. SymbolowiA
nadano liczbę 65 i\n
10. Nie ma to nic wspólnego ze sposobem przedstawiania łańcuchów znaków lub znaków na dysku lub w pliku tekstowym. UTF-8 jest specyfikacją (tj. Kodowaniem) tego, jak te liczby całkowite (tj. Symbole) są reprezentowane jako bajty (ciągi bitów), dzięki czemu można je jednoznacznie zapisać i odczytać z, powiedzmy, pliku.Odpowiedzi:
Technicznie robię:
String myString = "\u0048\u0065\u006C\u006C\u006F World";
automatycznie konwertuje go na
"Hello World"
, więc zakładam, że czytasz ciąg z jakiegoś pliku. Aby przekonwertować go na „Hello”, musisz przeanalizować tekst na oddzielne cyfry Unicode (weź\uXXXX
i po prostu pobierzXXXX
), a następnie wykonaj,Integer.ParseInt(XXXX, 16)
aby uzyskać wartość szesnastkową, a następnie tak,char
aby uzyskać właściwy znak.Edycja: trochę kodu, aby to osiągnąć:
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
źródło
Apache Commons Lang StringEscapeUtils.unescapeJava () można go rozszyfrować poprawnie.
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
źródło
Możesz użyć
StringEscapeUtils
z Apache Commons Lang , tj .:String Title = StringEscapeUtils.unescapeJava("\\u0048\\u0065\\u006C\\u006C\\u006F");
źródło
Ta prosta metoda zadziała w większości przypadków, ale spowoduje błąd o coś takiego jak „u005Cu005C”, które powinno zdekodować do ciągu „\ u0048”, ale w rzeczywistości zdekodowałoby „H”, ponieważ pierwszy przebieg daje „\ u0048” jako ciąg roboczy, następnie jest ponownie przetwarzany przez pętlę 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; }
źródło
Krótsza wersja:
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; }
źródło
StringEscapeUtils z biblioteki org.apache.commons.lang3 jest przestarzała od 3.6.
Możesz więc zamiast tego użyć ich nowej biblioteki tekstów wspólnych :
compile 'org.apache.commons:commons-text:1.9' OR <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-text</artifactId> <version>1.9</version> </dependency>
Przykładowy kod:
źródło
Nie jest to całkowicie jasne z twojego pytania, ale zakładam, że mówisz, że masz plik, w którym każda linia tego pliku jest nazwą pliku. A nazwa każdego pliku wygląda mniej więcej tak:
Innymi słowy, znaków w pliku z nazwami są
\
,u
,0
,0
,4
,8
i tak dalej.Jeśli tak, to to, co widzisz, jest oczekiwane. Java tłumaczy tylko
\uXXXX
sekwencje w literałach łańcuchowych w kodzie źródłowym (i podczas odczytywania przechowywanychProperties
obiektów). Kiedy odczytać zawartość złożyć trzeba będzie ciąg składający się ze znaków\
,u
,0
,0
,4
,8
i tak dalej, a nie łańcuchHello
.Będziesz więc musiał przeanalizować ten ciąg, aby wyodrębnić elementy
0048
,0065
itp., A następnie przekonwertować je nachar
si i utworzyć ciąg z tychchar
s, a następnie przekazać ten ciąg do procedury, która otwiera plik.źródło
Aktualizacje dotyczące odpowiedzi sugerujących użycie The Apache Commons Lang's: StringEscapeUtils.unescapeJava () - zostało wycofane,
Wymiana jest Apache Commons Text „s StringEscapeUtils.unescapeJava ()
źródło
Chciałem tylko dodać moją wersję, używając wyrażenia regularnego:
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());
źródło
Napisałem wydajne i odporne na błędy rozwiązanie:
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(); }
źródło
próbować
private static final Charset UTF_8 = Charset.forName("UTF-8"); private String forceUtf8Coding(String input) {return new String(input.getBytes(UTF_8), UTF_8))}
źródło
jeden łatwy sposób, w jaki znam używanie JsonObject:
try { JSONObject json = new JSONObject(); json.put("string", myString); String converted = json.getString("string"); } catch (JSONException e) { e.printStackTrace(); }
źródło
Oto moje rozwiązanie ...
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; } };
źródło
Szybki
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() }
źródło
Właściwie napisałem bibliotekę Open Source, która zawiera kilka narzędzi. Jednym z nich jest konwersja sekwencji Unicode na String i vice-versa. Uważam, że jest to bardzo przydatne. Oto cytat z artykułu o tej bibliotece o konwerterze Unicode:
Oto link do całego artykułu, który wyjaśnia, jakie narzędzia ma biblioteka i jak sprawić, by biblioteka z nich korzystała. Jest dostępny jako artefakt Mavena lub jako źródło z Github. Jest bardzo łatwy w użyciu. Biblioteka Java Open Source z filtrowaniem śladów stosu, konwerterem Unicode Silent String parsing i porównaniem wersji
źródło
W przypadku języka Java 9+ możesz użyć nowej metody replaceAll klasy 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); }
Uważam, że główną zaletą tego podejścia nad unescapeJava przez StringEscapeUtils (oprócz nie używając dodatkowej biblioteki) jest to, że można przekonwertować tylko znaki Unicode (jeśli chcesz), ponieważ ten ostatni konwertuje wszystkie uciekły znaki Java (jak \ n lub \ t ). Jeśli wolisz przekonwertować wszystkie znaki ucieczki, biblioteka jest naprawdę najlepszą opcją.
źródło
@NominSim Może istnieć inny znak, więc powinienem go wykryć na podstawie długości.
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(); }
źródło
UnicodeUnescaper
fromorg.apache.commons:commons-text
jest również akceptowalny.new UnicodeUnescaper().translate("\u0048\u0065\u006C\u006C\u006F World")
zwroty"Hello World"
źródło
Alternatywnym sposobem osiągnięcia tego może być użycie
chars()
wprowadzonego w Javie 9, można go użyć do iteracji po znakach, upewniając się, że każdy znak, który mapuje do zastępczego punktu kodowego, jest przepuszczany bez interpretacji. Można to wykorzystać jako: -String myString = "\u0048\u0065\u006C\u006C\u006F World"; myString.chars().forEach(a -> System.out.print((char)a)); // would print "Hello World"
źródło
Zauważyłem, że wiele odpowiedzi nie dotyczyło kwestii „znaków uzupełniających”. Oto właściwy sposób, aby go wesprzeć. Brak bibliotek innych firm, czysta implementacja 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
źródło
Rozwiązanie dla Kotlin:
val sourceContent = File("test.txt").readText(Charset.forName("windows-1251")) val result = String(sourceContent.toByteArray())
Kotlin używa wszędzie UTF-8 jako domyślnego kodowania.
Metoda
toByteArray()
ma domyślny argument -Charsets.UTF_8
.źródło
String(string.toByteArray())
nie osiąga dosłownie nic.toByteArray()
ma domyślny argument zCharsets.UTF_8
. Następnie tworzysz ciąg z bytearray z wymaganym kodowaniem. Zrobiłem dzisiaj test zwindows-1251
utf-8, to działa. Zrobiłem również porównanie na poziomie bajtów :)