Jak przekonwertować ciąg z kodowaniem Unicode na ciąg liter

84

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 \uXXXXw nazwie.

SharonBL
źródło
Jesteś pewien? Nie sądzisz, że znaki są po prostu drukowane jako znaki ucieczki Unicode?
Hot Licks
5
\u0048 jest H - są jednym i tym samym. Łańcuchy w Javie są w Unicode.
Hot Licks
Wydaje mi się, że problem może dotyczyć mojego interfejsu API Java do unix - ciąg, który otrzymuję, jest taki: \ u3123 \ u3255_nazwa_pliku.txt. A java tego nie ukrywa.
SharonBL
3
UTF-8 to kodowanie Unicode.
Pavel Radzivilovsky
5
To nie jest odpowiedź na twoje pytanie, ale pozwól mi wyjaśnić różnicę między Unicode a UTF-8, który wielu ludzi wydaje się mylić. Unicode jest zwłaszcza jeden do jednego mapowania między znakami, jakie znamy ( a, b, $, £, etc) do liczb całkowitych. Np. Symbolowi Anadano liczbę 65 i \n10. 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.
DustByte

Odpowiedzi:

49

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ź \uXXXXi po prostu pobierz XXXX), a następnie wykonaj, Integer.ParseInt(XXXX, 16)aby uzyskać wartość szesnastkową, a następnie tak, charaby 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
NominSim
źródło
Wydaje się, że to może być rozwiązanie. Czy masz pomysł, jak mogę to zrobić w Javie - czy mogę to zrobić za pomocą String.replaceAll lub czegoś podobnego?
SharonBL,
@SharonBL Zaktualizowałem kod, powinienem przynajmniej dać ci pomysł, od czego zacząć.
NominSim,
2
Bardzo dziękuję za pomoc! Znalazłem też inne rozwiązanie tego problemu: String s = StringEscapeUtils.unescapeJava ("\\ u20ac \\ n"); działa!
SharonBL,
2
podjąć próbę ponownego wynalezienia metod udostępnianych przez standardową bibliotekę Java. po prostu sprawdź czystą implementację stackoverflow.com/a/39265921/1511077
Evgeny Lebedev
1
Zawsze jestem zdumiony, gdy odpowiedź „ wymyśl na nowo koło ” otrzymuje tak wiele głosów.
Pedro Lobito
93

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
Tony
źródło
Ciąg sJava = "\ u0048 \\ u0065 \ u006C \ u006C \ u006F"; -----> Proszę dokonać prostej zmiany.
Shreyansh Shah
30

Możesz użyć StringEscapeUtilsz Apache Commons Lang , tj .:

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

Pedro Lobito
źródło
5
po dodaniu zależności w build.gradle: kompiluj 'commons-lang: commons-lang: 2.6' powyżej działa dobrze.
Joseph Mekwan,
8

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;
}
pasztet andrewski
źródło
próba ponownego wynalezienia metod udostępnianych przez Standardową bibliotekę Java. po prostu sprawdź czystą implementację stackoverflow.com/a/39265921/1511077
Evgeny Lebedev
1
Dzięki @EvgenyLebedev ... standardowa biblioteka wygląda dobrze i prawdopodobnie została dokładnie przetestowana, bardzo doceniona.
pasztet
7

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;
}
ssuukk
źródło
próba ponownego wynalezienia metod udostępnianych przez Standardową bibliotekę Java. po prostu sprawdź czystą implementację stackoverflow.com/a/39265921/1511077
Evgeny Lebedev
5

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:

org.apache.commons.text.StringEscapeUtils.unescapeJava(escapedString);
Bogdan Kobylynskyi
źródło
4

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:

\u0048\u0065\u006C\u006C\u006F

Innymi słowy, znaków w pliku z nazwami są \, u, 0, 0, 4, 8i tak dalej.

Jeśli tak, to to, co widzisz, jest oczekiwane. Java tłumaczy tylko \uXXXXsekwencje w literałach łańcuchowych w kodzie źródłowym (i podczas odczytywania przechowywanych Propertiesobiektów). Kiedy odczytać zawartość złożyć trzeba będzie ciąg składający się ze znaków \, u, 0, 0, 4, 8i tak dalej, a nie łańcuchHello .

Będziesz więc musiał przeanalizować ten ciąg, aby wyodrębnić elementy 0048, 0065itp., A następnie przekonwertować je na charsi i utworzyć ciąg z tych chars, a następnie przekazać ten ciąg do procedury, która otwiera plik.

QuantumMechanic
źródło
3

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());
robertokl
źródło
2

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();
}
neoedmund
źródło
1

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))}
Hao
źródło
1

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();
}
Ashkan Ghodrat
źródło
1

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;
                    }
                };
AndyW58
źródło
próbować wymyślić na nowo standardowe metody dostarczane przez standardową bibliotekę Java. po prostu sprawdź czystą implementację stackoverflow.com/a/39265921/1511077
Evgeny Lebedev
1

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()
    }
Wefyns
źródło
0

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:

Klasa StringUnicodeEncoderDecoder ma metody, które mogą konwertować String (w dowolnym języku) na sekwencję znaków Unicode i vice-versa. Na przykład łańcuch „Hello World” zostanie przekonwertowany na

„\ u0048 \ u0065 \ u006c \ u006c \ u006f \ u0020 \ u0057 \ u006f \ u0072 \ u006c \ u0064”

i mogą zostać przywrócone.

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

Michael Gantman
źródło
0

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ą.

Marcelo Barros
źródło
0

@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();
}
Jun.wan
źródło
0

UnicodeUnescaperfrom org.apache.commons:commons-textjest również akceptowalny.

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

anton
źródło
-1

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"
Naman
źródło
-1

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
lovestackh343
źródło
Nie działa, gdy w ciągu znaków znajdują się znaki inne niż Unicode, na przykład: href = \ u0022 \ / pl \ / blog \ / d-day-protection-europe-its-demons \ u0022 \ u003E \ n
Mohsen Abasi,
-1

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.

Evgeny Lebedev
źródło
nie jest to odpowiedź bez prawdziwych przykładów treści, których nie można „przekonwertować” za pomocą sugestii bytearray-way. czy możesz to zapewnić?
Evgeny Lebedev
String(string.toByteArray())nie osiąga dosłownie nic.
rustyx
Metoda @rustyx toByteArray()ma domyślny argument z Charsets.UTF_8. Następnie tworzysz ciąg z bytearray z wymaganym kodowaniem. Zrobiłem dzisiaj test z windows-1251utf-8, to działa. Zrobiłem również porównanie na poziomie bajtów :)
Evgeny Lebedev
@rustyx tutaj jest sedno dla Ciebie - gist.github.com/lebe-dev/31e31a3399c7885e298ed86810504676
Evgeny Lebedev