如何在Java中将UTF-8表示解析为字符串?

8

给定以下代码:

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

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

JDK已经提供了一些用于此目的的类吗?是否有一个库可以做到这一点?(最好是在maven下)

我尝试过使用ByteArrayOutputStream,但没有成功。


4
这不是UTF-8编码,只是一个包含Unicode转义表示的字符串。 - Ignacio Vazquez-Abrams
3个回答

3

首先,您是想解析字符串文字还是tmp将成为一些用户输入的数据?

如果这将是一个字符串字面量(即硬编码字符串),则可以使用Unicode转义进行编码。在您的情况下,这意味着只需使用单个反斜杠而不是双反斜杠:

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

然而,如果你需要使用Java的字符串解析规则来解析用户输入,一个好的起点可能是Apache Commons Lang的StringEscapeUtils.unescapeJava()方法。


3

这个代码可以处理ASCII字符,但是如果你使用ASCII码范围之外的Unicode字符,将会遇到问题(因为每个字符都被压缩成一个字节,而UTF-8允许每个字符占据一个完整的字)。下面的类型转换可以进行,因为你知道如果你保证输入基本上是ASCII字符(正如你在评论中提到的那样),那么UTF-8不会溢出一个字节。

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范围内的任何Unicode字符,而不仅仅是范围的前8位ASCII字符。感谢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)
        };
    }
}

1
“除UTF-8之外的Unicode字符”是什么?Unicode/UTF-8字符如何被“塞入一个字节”中?我不知道你是否意思正确,但是你表达得不够清楚,大部分都是错误的。 - deceze
如果您在字符串“str”中使用的是不同于UTF-8的Unicode字符集,则此代码可能无法正常工作。 UTF-8仍然使用8位,而其他Unicode字符集可能(很可能)使用超过8位(全部16位)。http://www.joelonsoftware.com/articles/Unicode.html - jmq
显然,一般情况下,这段代码是不够的。但在我的情况下,输入保证完全可翻译成ASCII。 - Stephan
@jmq 你的意思是源代码是否采用不同于UTF-8的字符集进行编码(我认为在Java中这并不重要)?因为,虽然我不太了解Java,但那些看起来像Unicode代码点,而不是UTF-8特定字节。http://kunststube.net/encoding/ - deceze
@jmq 嗯,你更正后的说法更有意义,但是 UTF-8 非 ASCII 字符使用多个字节。这段文本之所以能够运行,是因为它基本上只包含 ASCII 字符,但对于实际包含“Unicode字符”(即非 ASCII 字符)的情况,它将失败。 - deceze

1

我相信一定有更好的方法,但只使用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) 处理反斜杠转义(在首先使用 java.util.Properties.store(java.io.OutputStream, java.lang.String) 转义任何可能导致属性文件问题的内容,然后使用 replace("\\\\", "\\") 反转原始反斜杠的转义)。

(免责声明:尽管我测试了我能想到的所有情况,但仍然可能有一些我没有考虑到的情况。)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接