Як перетворити рядок із кодуванням Unicode у рядок літер


84

У мене є рядок із екранованими символами Unicode ,\uXXXX , і я хочу перетворити його на звичайні літери Unicode. Наприклад:

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

має стати

"Hello World"

Я знаю, що коли я друкую перший рядок, це вже відображається Hello world. Моя проблема полягає в тому, що я читаю імена файлів із файлу, а потім їх шукаю. Імена файлів у файлі захищаються за допомогою кодування Unicode, і коли я шукаю файли, я не можу їх знайти, оскільки він шукає файл, який має \uXXXXсвоє ім'я.


Ви впевнені? Ви не думаєте, що символи просто друкуються, коли втікає Unicode?
Hot Licks

5
\u0048 є H - вони одне і те ж. Рядки в Java є в Unicode.
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", тому я припускаю, що ви читаєте рядок з якогось файлу. Для того, щоб перетворити його на "Привіт", вам доведеться проаналізувати текст на окремі цифри 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і т.д. частини , а потім перетворювати їх в charс і зробити рядок з тих charз , а потім передати цей рядок в програмі , яка відкриває файл.



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 має методи, які можуть перетворити рядок (будь-якою мовою) у послідовність символів Unicode і навпаки. Наприклад, рядок "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 (наприклад, \ n або \ t ). Якщо ви віддаєте перевагу конвертувати всі екрановані символи, бібліотека - це дійсно найкращий варіант.


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, це може бути використано для перебору символів, переконуючись, що будь-який символ, який зіставляється із сурогатною кодовою точкою , передається через неінтерпретований. Це можна використовувати як: -

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 \ / en \ / blog \ / d-day-protecting-europe-its-demons \ u0022 \ u003E \ n
Мохсен

-1

Рішення для Kotlin:

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

Метод @rustyx toByteArray()має аргумент за замовчуванням з Charsets.UTF_8. Потім ви створюєте рядок з bytearray із необхідним кодуванням. Я зробив тест сьогодні з windows-1251utf-8, це працює. Також я зробив порівняння на рівні байтів :)
Євген Лебедєв

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.