如何将具有Unicode编码的字符串转换为字母字符串


82

我有一个带转义Unicode字符的字符串\uXXXX,我想将其转换为常规Unicode字母。例如:

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

应该成为

"Hello World"

我知道当我打印第一个字符串时它已经显示了Hello world。我的问题是我从文件中读取文件名,然后搜索它们。文件中的文件名使用Unicode编码进行转义,当我搜索文件时,我找不到它们,因为它会搜索\uXXXX名称相同的文件。


你确定?您不认为这些字符只是在Unicode转义时被打印出来?
Hot Licks 2012年

5
\u0048 H-它们是相同的。Java中的字符串使用Unicode。
Hot Licks 2012年

我猜问题可能出在我的Java到Unix api上-我得到的字符串类似于\ u3123 \ u3255_file_name.txt。而且Java不会隐藏它。
SharonBL 2012年

3
UTF-8unicode编码。
Pavel Radzivilovsky 2012年

5
这不是您问题的答案,但是让我澄清一下Unicode和UTF-8之间的区别,许多人似乎对此感到困惑。Unicode是一种特定的一到一个字符之间的映射,因为我们知道他们(ab$£,等)的整数。例如,符号A的编号为65,\n为10。这与在磁盘上或文本文件中表示字符串或字符的方式无关。UTF-8是如何将这些整数(即符号)表示为字节(位字符串)的规范(即编码),以便它们可以明确地写入文件或从文件中读取。
DustByte '16

Answers:


48

从技术上讲:

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 2012年

@SharonBL我更新了一些代码,至少应该让您知道从哪里开始。
NominSim 2012年

2
非常感谢您的帮助!我还找到了另一种解决方案:String s = StringEscapeUtils.unescapeJava(“ \\ u20ac \\ n”); 它完成了工作!
SharonBL 2012年

2
尝试重新发明标准Java库提供的方法。只需检查纯粹的实现stackoverflow.com/a/39265921/1511077
Evgeny Lebedev

1
当“重新发明轮子”的答案获得如此多的选票时,我总是感到惊讶。
Pedro Lobito '18

92

阿帕奇共享郎 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 2015年


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
Evgeny Lebedev

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
Evgeny Lebedev

5

从3.6版开始不推荐使用org.apache.commons.lang3库中的StringEscapeUtils 。

因此,您可以改用他们新的commons-text库:

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

换句话说,文件名的文件中的字符\u0048等。

如果是这样,那么您所看到的将是预期的。Java仅\uXXXX在源代码中(以及在读取存储的Properties对象时)转换字符串文字形式的序列。当你阅读的内容,你的文件,你将有包括字符的字符串\u0048等,并没有Hello

所以,你需要解析该字符串来提取00480065等片,然后将其转换为chars,并从这些字符串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());

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
Evgeny Lebedev

1

我写了一个高性能且防错的解决方案:

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

快速

 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具有可以将String(以任何语言)转换为Unicode字符序列的方法,反之亦然。例如,字符串“ Hello World”将转换为

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

并可以恢复原状。

这是整篇文章的链接,其中解释了该库具有的实用程序以及如何使该库使用它。它可以作为Maven工件或Github的源代码获得。这是非常容易使用。带有堆栈跟踪过滤,静默字符串解析Unicode转换器和版本比较的开源Java库


0

对于Java 9+,可以使用Matcher类的新replaceAll方法。

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

我相信这种做法在主要优势unescapeJavaStringEscapeUtils(除了不使用额外的库)是,你只能转换中的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

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

当字符串中包含非Unicode字符时,例如:href = \ u0022 \ / en \ / blog \ / d-day-protecting-europe-its-demons \ u0022 \ u003E \ n,此功能不起作用
Mohsen Abasi

-1

Kotlin解决方案:

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

Kotlin到处都使用UTF-8作为默认编码。

方法toByteArray()具有默认参数- Charsets.UTF_8


如果没有真正的内容示例,就无法解决问题,这些示例不能通过提示字节数组方式“转换”。你能提供吗?
Evgeny Lebedev

String(string.toByteArray())一无所获。
rustyx

@rustyx方法的toByteArray()默认参数为Charsets.UTF_8。然后,使用所需的编码从bytearray创建一个字符串。我今天windows-1251对utf-8进行了测试,它可以正常工作。我也做了字节级的比较:)
Evgeny Lebedev

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.