我有一个带有转义的Unicode 字符\uXXXX
的字符串,我想将它转换为常规的Unicode 字母。例如:
"\u0048\u0065\u006C\u006C\u006F World"
应该变成
"Hello World"
我知道当我打印第一个字符串时,它已经显示了Hello world
。我的问题是我从文件中读取文件名,然后搜索它们。文件中的文件名使用 Unicode 编码进行转义,当我搜索文件时,我找不到它们,因为它搜索名称中带有 \uXXXX
的文件。
【问题讨论】:
你确定吗?您不会认为字符只是作为 Unicode 转义符打印出来的吗?\u0048
is H
-- 它们是一回事。 Java 中的字符串采用 Unicode。
我猜问题可能出在我的 java 到 unix api 上——我得到的字符串类似于 \u3123\u3255_file_name.txt。而且java不会隐藏它。
UTF-8 是一种unicode编码。
这不是您问题的答案,但让我澄清一下 Unicode 和 UTF-8 之间的区别,很多人似乎混淆了。 Unicode 是我们所知道的字符(a
、b
、$
、£
等)到整数之间的特殊 一对一 映射。例如,符号 A
的编号为 65,\n
的编号为 10。这与字符串或字符在磁盘或文本文件中的表示方式无关。 UTF-8 是这些整数(即符号)如何表示为字节(位字符串)的规范(即编码),因此它们可以明确地从文件中写入和读取。
【参考方案1】:
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
【讨论】:
String sJava="\u0048\\u0065\u006C\u006C\u006F"; -----> 请做简单的改变。【参考方案2】:技术上做:
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 我更新了一些代码,至少应该让你知道从哪里开始。 非常感谢您的帮助!我还为此找到了另一个解决方案: String s = StringEscapeUtils.unescapeJava("\\u20ac\\n");它做的工作! 尝试重新发明标准 Java 库提供的方法。只需检查纯实现***.com/a/39265921/1511077 当“重新发明***”的答案获得如此多的选票时,我总是感到惊讶。【参考方案3】:您可以从Apache Commons Lang 使用StringEscapeUtils
,即:
String Title = StringEscapeUtils.unescapeJava("\\u0048\\u0065\\u006C\\u006C\\u006F");
【讨论】:
在 build.gradle 中添加依赖后:编译 'commons-lang:commons-lang:2.6' 以上工作正常。【参考方案4】:这种简单的方法适用于大多数情况,但会遇到像“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 库提供的方法。只需检查纯实现***.com/a/39265921/1511077 感谢@EvgenyLebedev ...标准库方式看起来不错,并且可能已经过彻底测试,非常感谢。【参考方案5】:短版:
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 库提供的方法。只需检查纯实现***.com/a/39265921/1511077【参考方案6】:org.apache.commons.lang3 库中的 StringEscapeUtils 从 3.6 开始为 deprecated。
所以您可以改用他们的新 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);
【讨论】:
【参考方案7】:您的问题并不完全清楚,但我假设您说您有一个文件,其中该文件的每一行都是一个文件名。每个文件名都是这样的:
\u0048\u0065\u006C\u006C\u006F
也就是说文件名的文件中的字符是\
、u
、0
、0
、4
、8
等等。
如果是这样,那么您所看到的就是预期的。 Java 只翻译源代码中字符串文字中的\uXXXX
序列(以及在读取存储的Properties
对象时)。当您阅读您归档的内容时,您将看到一个由字符\
、u
、0
、0
、4
、8
等组成的字符串,not 字符串Hello
。
因此,您需要解析该字符串以提取0048
、0065
等片段,然后将它们转换为char
s 并从这些char
s 中创建一个字符串,然后将该字符串传递给打开文件的例程。
【讨论】:
【参考方案8】:有关建议使用 The Apache Commons Lang's 的答案的更新: StringEscapeUtils.unescapeJava() - 它已被弃用,
已弃用。 从 3.6 开始,请改用 commons-text StringEscapeUtils
替换的是 Apache Commons Text 的 StringEscapeUtils.unescapeJava()
【讨论】:
【参考方案9】:只是想贡献我的版本,使用正则表达式:
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());
【讨论】:
【参考方案10】:我写了一个高效且防错的解决方案:
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();
【讨论】:
【参考方案11】:对于 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);
我相信这种方法比StringEscapeUtils 的unescapeJava 的主要优势(除了不使用额外的库)是您只能转换 unicode 字符(如果您愿意),因为后者会转换所有转义的 Java 字符(如 \n 或 \t)。如果您希望转换所有转义字符,那么该库确实是最佳选择。
【讨论】:
【参考方案12】:试试
private static final Charset UTF_8 = Charset.forName("UTF-8");
private String forceUtf8Coding(String input) return new String(input.getBytes(UTF_8), UTF_8))
【讨论】:
【参考方案13】:我知道使用 JsonObject 的一种简单方法:
try
JSONObject json = new JSONObject();
json.put("string", myString);
String converted = json.getString("string");
catch (JSONException e)
e.printStackTrace();
【讨论】:
【参考方案14】:这是我的解决方案...
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 库提供的标准方法。只需检查纯实现***.com/a/39265921/1511077【参考方案15】:快
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()
【讨论】:
【参考方案16】:使用 Kotlin,您可以为 String
编写自己的扩展函数fun String.unescapeUnicode() = replace("\\\\u([0-9A-Fa-f]4)".toRegex())
String(Character.toChars(it.groupValues[1].toInt(radix = 16)))
然后
fun main()
val originalString = "\\u0048\\u0065\\u006C\\u006C\\u006F World"
println(originalString.unescapeUnicode())
【讨论】:
【参考方案17】:实际上,我编写了一个包含一些实用程序的开源库。其中之一是将 Unicode 序列转换为字符串,反之亦然。我发现它非常有用。以下是关于这个库关于 Unicode 转换器的文章的引用:
StringUnicodeEncoderDecoder 类具有可以转换 将字符串(任何语言)转换为 Unicode 字符序列和 反之亦然。例如一个字符串“Hello World”将被转换成
"\u0048\u0065\u006c\u006c\u006f\u0020 \u0057\u006f\u0072\u006c\u0064"
并且可能会恢复。
这里是整篇文章的链接,它解释了库有哪些实用程序以及如何让库使用它。它可以作为 Maven 工件或从 Github 获得。这是非常容易使用。 Open Source Java library with stack trace filtering, Silent String parsing Unicode converter and Version comparison
【讨论】:
【参考方案18】:@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();
【讨论】:
【参考方案19】:来自org.apache.commons:commons-text
的UnicodeUnescaper
也是可以接受的。
new UnicodeUnescaper().translate("\u0048\u0065\u006C\u006C\u006F World")
返回"Hello World"
【讨论】:
【参考方案20】:实现此目的的另一种方法是利用 Java 9 引入的 chars()
,这可用于迭代字符,确保映射到 surrogate code point 的任何字符都未经解释地传递。这可以用作:-
String myString = "\u0048\u0065\u006C\u006C\u006F World";
myString.chars().forEach(a -> System.out.print((char)a));
// would print "Hello World"
【讨论】:
【参考方案21】:我发现很多答案都没有解决“补充字符”的问题。这是支持它的正确方法。无第三方库,纯 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【参考方案22】: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 方法toByteArray()
的默认参数为Charsets.UTF_8
。然后,您从 bytearray 创建一个具有所需编码的字符串。我今天用windows-1251
对utf-8 进行了测试,它有效。我也在字节级别进行了比较:)
@rustyx 这里是给你的要点 - gist.github.com/lebe-dev/31e31a3399c7885e298ed86810504676