Java 中非 ASCII 字符的 URL 解码

2024-05-04

我正在尝试用 Java 解码包含 % 编码字符的 URL

我尝试使用 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 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 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"));

根据测试字符串,我使用不同的编码得到正确的结果:

  • 对于 %C3%A7,我按预期得到了使用“UTF-8”编码的正确结果,使用“ISO-8859-1”或“WINDOWS-1252”编码得到了错误的结果
  • 对于%E4,则相反。

对于这两个测试 URL,如果我将它们放入 Chrome 地址栏中,我会得到正确的页面。

如何在所有情况下正确解码 URL? 谢谢你的帮助

====回答====

感谢下面麦克道尔回答中的建议,它现在似乎有效。这是我现在的代码:

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

Anv%E4ndare

As 波波菲波 说 https://stackoverflow.com/a/21905895/304这不是有效的 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- 不是有效的序列UTF-8 方案 http://en.wikipedia.org/wiki/Utf_8#Description并且解码器可以检测到这一点。

如果颠倒编码顺序,第一个字符串可以愉快地(但错误地)解码为 ISO-8859-1。


Note: HTTP 不关心 https://www.rfc-editor.org/rfc/rfc2616#section-3.2关于百分比编码,您可以编写一个接受的 Web 服务器http://foo/%%%%%作为有效的形式。这URI spec https://www.rfc-editor.org/rfc/rfc3986强制使用 UTF-8,但这是追溯性的。实际上由服务器来描述其 URI 的形式,如果您必须处理任意 URI,则需要了解这一遗留问题。

我写了一点有关 URL 和 Java 的更多信息请参见此处 http://illegalargumentexception.blogspot.co.uk/2009/12/java-safe-character-handling-and-url.html.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 中非 ASCII 字符的 URL 解码 的相关文章

随机推荐

  • 是否值得购买 Mahout in Action 以跟上 Mahout 的速度,或者还有其他更好的来源吗?

    我目前是一个非常随意的用户阿帕奇马胡特 http mahout apache org 我正在考虑购买这本书象夫在行动 http www manning com owen 不幸的是 我很难理解这本书的价值 并且认为它是一本曼宁早期访问计划 h
  • 如何在ArangoDB中设置集群和分片?

    我想在arangoDB中使用分片 我已经制作了协调器 DBServers 如文档2 8 5中所述 但仍然有人可以详细解释它 以及我如何能够在分片之后和之前检查查询的性能 可以测试您的应用程序对于本地集群 所有实例都在一台机器上运行吗 htt
  • 来自 Toplink 表达式的 SQL 查询

    我有一个 oracle toplink expressions Expression 表达式对象 它是使用 oracle toplink expressions ExpressionBuilder 创建的 我想找到它的等效 SQL 查询 比
  • 智能表 - 预选特定行

    我正在使用智能表 我需要预先选择特定行 因此 在加载我的列表后 我循环进入它并设置isSelected当我到达我想要选择的项目时属性 Preselect a row for var i 0 len scope displayCollecti
  • 多线程文件写入

    我正在尝试使用多个线程写入大文件的不同部分 就像分段文件下载器所做的那样 我的问题是 执行此操作的安全方法是什么 我是否打开文件进行写入 创建线程 将 Stream 对象传递给每个线程 我不希望发生错误 因为多个线程可能同时访问同一个对象
  • 如何使用ajax从服务器接收返回的数据?

    基本上我有一个带有用户名文本框和提交按钮的表单 现在我想要的是 当用户在文本框中输入文本时 它应该获取文本框值并将用户名发送到服务器 以便服务器可以检查该用户名是否被任何其他用户占用 我可以将文本值发送到服务器 但我不知道如何接收回一些数据
  • 精简 Liferay:删除 Hibernate 和其他未使用的应用程序

    我有一些有关 Liferay 与 hibernate 交互的问题 由于某些原因 我们希望从应用程序中消除这一层 因此有以下问题 我知道Liferay是与Hibernate一起打包的 并且通过使用Hibernate API简化了对Lifera
  • ContentPane 和 JPanel 之间有什么关系?

    我发现了一个示例 其中将按钮添加到面板 实例JPanel 然后将面板添加到容器中 由getContentPane 然后容器通过构造被包含到JFrame 窗户 我尝试了两件事 我把容器扔掉了 更详细地说 我向面板添加了按钮 实例JPanel
  • ServletContext 和 Session 对象

    我们从请求对象 HttpServletRequest 获得的 ServletContext 和 Session 对象的行为是否相同 会话是特定于用户的 Servlet 上下文本质上是全局的 在该 Servlet 的上下文内 这意味着访问该
  • 在 Java 中从字符串中提取第一个单词的最佳方法是什么?

    尝试编写一个简短的方法 以便我可以解析字符串并提取第一个单词 我一直在寻找最好的方法来做到这一点 我想我会用str split 但是我想从字符串中获取第一个单词 并将其保存在一个变量中 然后将其余的标记放入另一个变量中 有没有一种简洁的方法
  • 是否可以使 font-weight:bold 等于 500 而不是 700?

    我刚刚使用 Google Fonts 并发现了 Fira Sans 字体 很好 但我不喜欢 Bold 700 风格 它太大胆了 不符合我的喜好 但是 如果我选择中 500 样式 浏览器不会将其用于任何设置为font weight bold
  • 在python中求Legendre多项式的根

    我正在编写一个程序 通过勒让德 高斯求积求解积分 n 阶求积算法需要在某一时刻找到 n 阶勒让德多项式 Pn x 的根 并将它们分配给数组 Absc 表示 横坐标 Pn 是一个 n 阶多项式 在区间 1 1 上有 n 个独立的实根 我希望能
  • 在 Woocommerce 中以编程方式创建新产品属性

    如何通过插件为 WooCommerce 创建属性 我只找到 wp set object terms object id terms taxonomy append From 这个堆栈问题 https stackoverflow com qu
  • 将动态库 (.dylib) 复制到框架 (.framework)

    我有两个 XCode 项目 一个框架和一个客户端应用程序 我的应用程序依赖于我的框架 一切都运行良好 每次应用程序都会重新编译框架 项目构建路径设置正确 完全没问题 现在框架开始使用第 3 方 dylib 文件 并且它与 dylib 链接
  • 允许任何内容的 XML 架构 (xsd:any)

    我需要一个允许任何事情的 XML 模式示例 这可能听起来很奇怪 但我需要它来调试我当前的模式 问题是 我有一个在函数 我无法控制的 DLL 的一部分 中使用的复杂对象以及模式 并且该函数返回 XML 目前 该函数会抛出异常 因为在验证模式时
  • 对 java 9 ServiceLoader::load 方法以及如何提供服务实现的方式感到困惑

    在这个java文档中 https docs oracle com javase 9 docs api java util ServiceLoader html https docs oracle com javase 9 docs api
  • Hadoop安装问题:

    我跟着this http www bogotobogo com Hadoop BigData hadoop Install on ubuntu single node cluster phpHadoop 安装教程 不幸的是 当我运行全部启动
  • Python:“取消导入”、“重新导入”、“重置导入”?

    我调试 在 PyCharm 中 一个脚本 我在断点处停止 然后转到调试控制台窗口 然后从那里调用导入行 如下所示 import my util1 from my utils 然后我调用 my util1 到目前为止 一切都还好 然后我更改
  • GitHub API - 语言列表

    GitHub API 中是否有一个端点可以为我提供 GitHub 上所有语言的列表 我正在寻找与 github com 网站上 趋势 部分中的语言下拉列表类似的结果 不是直接来自 GitHub API The OP敏捷王牌 https st
  • Java 中非 ASCII 字符的 URL 解码

    我正在尝试用 Java 解码包含 编码字符的 URL 我尝试使用 java net URI 类来完成这项工作 但它并不总是正常工作 String test https fr wikipedia org wiki Fondation Alli