Как разобрать представление UTF-8 в строку в Java? - PullRequest
8 голосов
/ 15 февраля 2012

Учитывая следующий код:

String tmp = new String("\\u0068\\u0065\\u006c\\u006c\\u006f\\u000a");

String result = convertToEffectiveString(tmp); // result contain now "hello\n"

Предоставляет ли JDK некоторые классы для этого?Есть ли библиотека, которая делает это?(желательно под maven)

Я пытался с ByteArrayOutputStream безуспешно.

Ответы [ 3 ]

3 голосов
/ 15 февраля 2012

Это работает, но только с ASCII. Если вы используете символы Юникода вне диапазона ASCCI, то у вас будут проблемы (поскольку каждый символ вставляется в байт вместо полного слова, разрешенного UTF-8). Вы можете выполнить приведение ниже, потому что вы знаете, что UTF-8 не будет переполнен ни на один байт, если вы гарантировали, что ввод в основном ASCII (как вы упомянули в ваших комментариях).

package sample;

import java.io.UnsupportedEncodingException;

public class UnicodeSample {
    public static final int HEXADECIMAL = 16;

    public static void main(String[] args) {

        try {
            String str = "\\u0068\\u0065\\u006c\\u006c\\u006f\\u000a";

            String arr[] = str.replaceAll("\\\\u"," ").trim().split(" ");
            byte[] utf8 = new byte[arr.length];

            int index=0;
            for (String ch : arr) {
                utf8[index++] = (byte)Integer.parseInt(ch,HEXADECIMAL);
            }

            String newStr = new String(utf8, "UTF-8");
            System.out.println(newStr);

        }
        catch (UnsupportedEncodingException e) {
            // handle the UTF-8 conversion exception
        }
    }
}

Вот еще одно решение, которое устраняет проблему работы только с символами ASCII. Это будет работать с любыми символами Юникода в диапазоне UTF-8 вместо ASCII только в первых 8 битах диапазона. Спасибо Deceze за вопросы. Вы заставили меня больше думать о проблеме и решении.

package sample;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;

public class UnicodeSample {
    public static final int HEXADECIMAL = 16;

    public static void main(String[] args) {

        try {
            String str = "\\u0068\\u0065\\u006c\\u006c\\u006f\\u000a\\u3fff\\uf34c";

            ArrayList<Byte> arrList = new ArrayList<Byte>();
            String codes[] = str.replaceAll("\\\\u"," ").trim().split(" ");

            for (String c : codes) {

                int code = Integer.parseInt(c,HEXADECIMAL);
                byte[] bytes = intToByteArray(code);

                for (byte b : bytes) {
                    if (b != 0) arrList.add(b);
                }
            }

            byte[] utf8 = new byte[arrList.size()];
            for (int i=0; i<arrList.size(); i++) utf8[i] = arrList.get(i);

            str = new String(utf8, "UTF-8");
            System.out.println(str);
        }
        catch (UnsupportedEncodingException e) {
            // handle the exception when
        }
    }

    // Takes a 4 byte integer and and extracts each byte
    public static final byte[] intToByteArray(int value) {
        return new byte[] {
                (byte) (value >>> 24),
                (byte) (value >>> 16),
                (byte) (value >>> 8),
                (byte) (value)
        };
    }
}
3 голосов
/ 15 февраля 2012

Во-первых, вы просто пытаетесь разобрать строковый литерал, или tmp будет введенными пользователем данными?

Если это будет строковый литерал (то есть жестко закодированная строка), он может быть закодирован с использованием экранирования Unicode. В вашем случае это просто означает использование одинарных обратных косых черт вместо двойных обратных:

String result = "\u0068\u0065\u006c\u006c\u006f\u000a";

Если, однако, вам нужно использовать правила разбора строк Java для анализа ввода пользователя, хорошей отправной точкой может быть метод StringEscapeUtils.unescapeJava () , разработанный Apache Commons Lang (*).

1 голос
/ 15 февраля 2012

Я уверен, что должен быть лучшим способом, но используя только JDK:

public static String handleEscapes(final String s)
{
    final java.util.Properties props = new java.util.Properties();
    props.setProperty("foo", s);
    final java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
    try
    {
        props.store(baos, null);
        final String tmp = baos.toString().replace("\\\\", "\\");
        props.load(new java.io.StringReader(tmp));
    }
    catch(final java.io.IOException ioe) // shouldn't happen
        { throw new RuntimeException(ioe); }
    return props.getProperty("foo");
}

использует java.util.Properties.load(java.io.Reader) для обработки обратной косой черты-Escape (после первого использования java.util.Properties.store(java.io.OutputStream, java.lang.String) для экранирования от обратной косой черты всего, что может вызвать проблемы в файле свойств, а затем с помощью replace("\\\\", "\\") для обратной экранирования от обратной косой черты исходных обратных косых черт).

(Отказ от ответственности: несмотря на то, что я проверял все случаи, о которых мог подумать, все еще, вероятно, есть такие, о которых я не думал .)

...