我正在尝试用Java解码包含%编码字符的网址
我尝试过使用java.net.URI类来完成这项工作,但它并不总能正常工作。
String test = "https://fr.wikipedia.org/wiki/Fondation_Alliance_fran%C3%A7aise";
URI uri = new URI(test);
System.out.println(uri.getPath());
对于测试字符串“https://fr.wikipedia.org/wiki/Fondation_Alliance_fran%C3%A7aise”,结果是正确的“/ wiki/Fondation_Alliance_française”(%C3%A7被ç正确替换)。
但是对于其他一些测试字符串,例如“http://sv.wikipedia.org/wiki/Anv%E4ndare:Lsjbot/Statistik#Drosophilidae”,它会给出错误的结果“/ wiki /Anv ndare:Lsjbot / Statistik”(%E4替换为 而不是ä)。
我使用getRawPath()和URLDecoder类进行了一些测试。
System.out.println(URLDecoder.decode(uri.getRawPath(), "UTF8"));
System.out.println(URLDecoder.decode(uri.getRawPath(), "ISO-8859-1"));
System.out.println(URLDecoder.decode(uri.getRawPath(), "WINDOWS-1252"));
根据测试字符串,我使用不同的编码得到正确的结果:
对于这两个测试网址,如果我将其放入Chrome地址栏,我会获得正确的网页。
如何在所有情况下正确解码网址? 谢谢你的帮助
====回答====
感谢McDowell在下面的回答中提出的建议,现在似乎有效了。这就是我现在所拥有的代码:
private static void appendBytes(ByteArrayOutputStream buf, String data) throws UnsupportedEncodingException {
byte[] b = data.getBytes("UTF8");
buf.write(b, 0, b.length);
}
private static byte[] parseEncodedString(String segment) throws UnsupportedEncodingException {
ByteArrayOutputStream buf = new ByteArrayOutputStream(segment.length());
int last = 0;
int index = 0;
while (index < segment.length()) {
if (segment.charAt(index) == '%') {
appendBytes(buf, segment.substring(last, index));
if ((index < segment.length() + 2) &&
("ABCDEFabcdef0123456789".indexOf(segment.charAt(index + 1)) >= 0) &&
("ABCDEFabcdef0123456789".indexOf(segment.charAt(index + 2)) >= 0)) {
buf.write((byte) Integer.parseInt(segment.substring(index + 1, index + 3), 16));
index += 3;
} else if ((index < segment.length() + 1) &&
(segment.charAt(index + 1) == '%')) {
buf.write((byte) '%');
index += 2;
} else {
buf.write((byte) '%');
index++;
}
last = index;
} else {
index++;
}
}
appendBytes(buf, segment.substring(last));
return buf.toByteArray();
}
private static String parseEncodedString(String segment, Charset... encodings) {
if ((segment == null) || (segment.indexOf('%') < 0)) {
return segment;
}
try {
byte[] data = parseEncodedString(segment);
for (Charset encoding : encodings) {
try {
if (encoding != null) {
return encoding.newDecoder().
onMalformedInput(CodingErrorAction.REPORT).
decode(ByteBuffer.wrap(data)).toString();
}
} catch (CharacterCodingException e) {
// Incorrect encoding, try next one
}
}
} catch (UnsupportedEncodingException e) {
// Nothing to do
}
return segment;
}
答案 0 :(得分:2)
ANV%E4ndare
作为PopoFibo says,这不是有效的UTF-8编码序列。
你可以做一些宽容的最佳猜测解码:
public static String parse(String segment, Charset... encodings) {
byte[] data = parse(segment);
for (Charset encoding : encodings) {
try {
return encoding.newDecoder()
.onMalformedInput(CodingErrorAction.REPORT)
.decode(ByteBuffer.wrap(data))
.toString();
} catch (CharacterCodingException notThisCharset_ignore) {}
}
return segment;
}
private static byte[] parse(String segment) {
ByteArrayOutputStream buf = new ByteArrayOutputStream();
Matcher matcher = Pattern.compile("%([A-Fa-f0-9][A-Fa-f0-9])")
.matcher(segment);
int last = 0;
while (matcher.find()) {
appendAscii(buf, segment.substring(last, matcher.start()));
byte hex = (byte) Integer.parseInt(matcher.group(1), 16);
buf.write(hex);
last = matcher.end();
}
appendAscii(buf, segment.substring(last));
return buf.toByteArray();
}
private static void appendAscii(ByteArrayOutputStream buf, String data) {
byte[] b = data.getBytes(StandardCharsets.US_ASCII);
buf.write(b, 0, b.length);
}
此代码将成功解码给定的字符串:
for (String test : Arrays.asList("Fondation_Alliance_fran%C3%A7aise",
"Anv%E4ndare")) {
String result = parse(test, StandardCharsets.UTF_8,
StandardCharsets.ISO_8859_1);
System.out.println(result);
}
请注意,这不是一个让您忽略正确的URL编码的万无一失的系统。它在这里工作是因为 v%E4n - 字节序列76 E4 6E
- 不是the UTF-8 scheme的有效序列,解码器可以检测到它。
如果您颠倒编码的顺序,第一个字符串可以愉快地(但不正确地)解码为ISO-8859-1。
注意:HTTP doesn't care关于百分比编码,您可以编写一个接受http://foo/%%%%%
作为有效表单的Web服务器。 URI spec强制要求使用UTF-8,但这是追溯性的。服务器真的应该描述它的URI应该是什么形式,如果你必须处理任意URI,你需要知道这些遗产。