Как преобразовать строку с кодировкой Unicode в строку букв


84

У меня есть строка с сбежавшим Unicode символами, \uXXXXи я хочу , чтобы преобразовать его в обычные буквы Unicode. Например:

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

должен стать

"Hello World"

Я знаю, что когда я печатаю первую строку, она уже отображается Hello world. Моя проблема в том, что я читаю имена файлов из файла, а затем ищу их. Имена файлов в файле экранированы кодировкой Unicode, и когда я ищу файлы, я не могу их найти, поскольку он ищет файл с \uXXXXименем.


Ты уверен? Вы не думаете, что символы просто печатаются как escape-последовательности Unicode?
Hot Licks

5
\u0048 есть H - они одно и то же. Строки в Java находятся в Юникоде.
Hot Licks

Я предполагаю, что проблема может быть связана с моим java для unix api - строка, которую я получаю, выглядит примерно так \ u3123 \ u3255_file_name.txt. И Java не скрывает этого.
SharonBL

3
UTF-8 - это кодировка Unicode.
Павел Радзивиловский

5
Это не ответ на ваш вопрос, но позвольте мне прояснить разницу между Unicode и UTF-8, которую многие люди, кажется, путают. Unicode является особенно один-к-одному отображение между символами , как мы их знаем ( a, b, $, £и т.д.) для целых чисел. Например, символу Aприсваивается номер 65, а \nэто 10. Это не имеет ничего общего с тем, как строки или символы представлены на диске или, скажем, в текстовом файле. UTF-8 - это спецификация (т. Е. Кодировка) того, как эти целые числа (т. Е. Символы) представлены как байты (битовые строки), чтобы их можно было однозначно записать и прочитать, скажем, из файла.
DustByte

Ответы:


49

Технически делаю:

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

автоматически преобразует его в "Hello World", поэтому я предполагаю, что вы читаете строку из какого-то файла. Чтобы преобразовать его в «Hello», вам нужно будет разобрать текст на отдельные цифры Unicode (взять \uXXXXи просто получить XXXX), затем сделать, Integer.ParseInt(XXXX, 16)чтобы получить шестнадцатеричное значение, а затем ввести его, charчтобы получить фактический символ.

Изменить: код для этого:

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

Кажется, это может быть решением. У вас есть идея, как я могу это сделать в java - могу ли я сделать это с помощью String.replaceAll или что-то в этом роде?
SharonBL

@SharonBL Я обновил код, по крайней мере, должен дать вам представление о том, с чего начать.
NominSim

2
Большое спасибо за вашу помощь! Я также нашел другое решение для этого: String s = StringEscapeUtils.unescapeJava ("\\ u20ac \\ n"); он делает свою работу!
SharonBL

2
попытаться заново изобрести методы, предоставляемые стандартной библиотекой Java. просто проверьте чистую реализацию stackoverflow.com/a/39265921/1511077
Евгений Лебедев

1
Я всегда удивляюсь, когда за ответ « изобретать велосипед » набирают столько голосов.
Педро Лобито

93

Apache Commons Lang StringEscapeUtils.unescapeJava () может декодировать его должным образом.

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

Строка sJava = "\ u0048 \\ u0065 \ u006C \ u006C \ u006F"; -----> Пожалуйста, сделайте простое изменение.
Shreyansh Shah


8

Этот простой метод будет работать в большинстве случаев, но может привести к ошибке типа «u005Cu005C», который должен декодировать строку «\ u0048», но на самом деле будет декодировать «H», поскольку первый проход создает «\ u0048» как рабочую строку, которая затем снова обрабатывается циклом 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;
}

попытаться заново изобрести методы, предоставляемые стандартной библиотекой Java. просто проверьте чистую реализацию stackoverflow.com/a/39265921/1511077
Евгений Лебедев

1
Спасибо @EvgenyLebedev ... стандартный библиотечный способ выглядит хорошо и, по-видимому, был тщательно протестирован, очень признателен.
Эндрю Пэйт

7

Укороченная версия:

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;
}

попытаться заново изобрести методы, предоставляемые стандартной библиотекой Java. просто проверьте чистую реализацию stackoverflow.com/a/39265921/1511077
Евгений Лебедев

5

StringEscapeUtils из библиотеки org.apache.commons.lang3 устарел с версии 3.6.

Таким образом, вы можете вместо этого использовать их новую библиотеку обычного текста :

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

OR

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

Пример кода:

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

4

Это не совсем ясно из вашего вопроса, но я предполагаю, что вы говорите, что у вас есть файл, в котором каждая строка этого файла является именем файла. И каждое имя файла выглядит примерно так:

\u0048\u0065\u006C\u006C\u006F

Другими словами, символы в файле имен файлов \, u, 0, 0, 4, 8и так далее.

Если да, то то, что вы видите, ожидаемо. Java переводит \uXXXXпоследовательности только в строковых литералах в исходном коде (и при чтении сохраненных Propertiesобъектов). Когда вы читаете содержимое, файл , который вы будете иметь строку , состоящую из символов \, u, 0, 0, 4, 8и так далее и не строка Hello.

Таким образом, вам нужно будет проанализировать эту строку, чтобы извлечь части 0048, 0065и т. Д., А затем преобразовать их в chars и создать строку из этих chars, а затем передать эту строку в процедуру, которая открывает файл.



3

Просто хотел внести свою версию, используя регулярное выражение:

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());

2

Я написал эффективное и защищенное от ошибок решение:

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

пытаться

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

один простой способ, который я знаю, используя JsonObject:

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

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

1

Вот мое решение ...

                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;
                    }
                };

попытаться заново изобрести стандартные методы, предоставляемые стандартной библиотекой Java. просто проверьте чистую реализацию stackoverflow.com/a/39265921/1511077
Евгений Лебедев

1

Быстрый

 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

Собственно, я написал библиотеку с открытым исходным кодом, которая содержит некоторые утилиты. Один из них - преобразование последовательности Unicode в String и наоборот. Я нахожу это очень полезным. Вот цитата из статьи об этой библиотеке о конвертере Unicode:

Класс StringUnicodeEncoderDecoder имеет методы, которые могут преобразовывать строку (на любом языке) в последовательность символов Юникода и наоборот. Например, строка «Hello World» будет преобразована в

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

и может быть восстановлен обратно.

Вот ссылка на всю статью, в которой объясняется, какие утилиты есть в библиотеке и как заставить библиотеку их использовать. Он доступен как артефакт Maven или как исходный код на Github. Он очень прост в использовании. Библиотека Java с открытым исходным кодом с фильтрацией трассировки стека, конвертером Unicode для синтаксического анализа строк и сравнением версий


0

Для Java 9+ вы можете использовать новый метод replaceAll класса 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);
}

Я считаю , что главное преимущество этого подхода над unescapeJava по StringEscapeUtils (кроме того , не используя дополнительную библиотеку) является то , что вы можете конвертировать только символы Unicode (если вы хотите), поскольку последняя преобразует все сбежавших Java символы (например , \ п или \ т ). Если вы предпочитаете преобразовывать все экранированные символы, библиотека действительно лучший вариант.


0

@NominSim Может быть другой символ, поэтому я должен определить его по длине.

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

UnicodeUnescaperиз org.apache.commons:commons-textтоже приемлемо.

new UnicodeUnescaper().translate("\u0048\u0065\u006C\u006C\u006F World") возвращается "Hello World"


-1

Альтернативный способ достижения этого может заключаться в использовании chars()введенного в Java 9, его можно использовать для итерации по символам, гарантируя, что любой char, который сопоставляется с суррогатной кодовой точкой , передается неинтерпретированным. Это можно использовать как: -

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

-1

Я обнаружил, что многие ответы не касались вопроса «дополнительных символов». Вот правильный способ поддержать это. Никаких сторонних библиотек, чистая реализация 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

Не работает , когда есть символы не Юникода внутри строки, такие как: HREF = \ u0022 \ / ан \ / блог \ / d-день защиты-Европа-его-демоны \ u0022 \ u003e \ п
Мохсен Абаси

-1

Решение для Котлина:

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

Kotlin везде использует UTF-8 в качестве кодировки по умолчанию.

У метода toByteArray()есть аргумент по умолчанию - Charsets.UTF_8.


это не ответ без реальных примеров контента, который нельзя «преобразовать» с помощью метода байтового массива. вы можете это предоставить?
Евгений Лебедев

String(string.toByteArray())буквально ничего не добивается.
rustyx 07

Метод @rustyx toByteArray()имеет аргумент по умолчанию с Charsets.UTF_8. Затем вы создаете строку из массива байтов с требуемой кодировкой. Я тестировал сегодня с windows-1251UTF-8, он работает. Также я сделал сравнение на байтовом уровне :)
Евгений Лебедев

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.