【问题标题】:File.toURI does not encode plus signFile.toURI 不编码加号
【发布时间】:2017-10-09 10:30:30
【问题描述】:

我只是想在这里用这个问题检查我自己的理智。我有一个文件名,其中包含 +(加号)字符,这在某些操作系统和文件系统(例如 MacOS 和 HFS+)上完全有效。

但是,我发现java.io.File#toURI() 无法正常运行。

例如:

new File("hello+world.txt").toURI().toString()

在我的 Mac 机器上返回:

file:/Users/aretter/code/rocksdb/hello+world.txt

但是恕我直言,这是不正确的,因为文件名中的 +(加号)字符尚未在 URI 中编码。 URI 根本不代表原始文件名,URI 中的 + 与文件名中的 + 字符的含义截然不同。

因此,如果我们解码 URI,加号现在将被替换为 (空格)字符,我们丢失了信息。例如:

URLDecoder.decode(new File("hello+world.txt").toURI().toURL().toString)

结果:

file:/Users/aretter/code/rocksdb/hello world.txt

我原本期望的会是这样的:

new File("hello+world.txt").toURI().toString()

导致:

file:/Users/aretter/code/rocksdb/hello%2Bworld.txt

这样当它以后被使用和解码时,加号会被保留。

我很难相信 Java SE 中可能存在如此明显的错误。谁能指出我错在哪里?

另外,如果有解决方法,我想听听吗?请记住,我实际上并没有将静态字符串作为文件名提供给 File,而是从磁盘读取文件目录,其中一些文件可能包含 +(加号)字符。

【问题讨论】:

  • 如果我没看错你的问题,你希望hello+world.txt 显示为hello%2Bworld.txt

标签: java file url uri


【解决方案1】:

让我试着澄清一下,

  • '+' 加号用作编码字符,用于在 HTML 表单(又名 application/x-www-form-urlencoded MIME 格式)的上下文中对 ' ' 空间进行编码。
  • '%20' 字符用作编码字符,以在 URL/URI 格式的上下文中对 ' ' 空间进行编码。

'+' 加号作为 URL 上下文中的普通字符是威胁,它不以任何形式编码(例如 %20)。

因此,当您调用 new File("hello+world.txt").toURI().toString() 时,不会对“+”字符执行任何编码(只是因为它不是必需的)。

现在来到URLDecoderthis class 是一个用于 HTML 表单解码的实用程序类。它将“+”加号视为编码字符,因此将其解码为“”空格字符。在您的示例中,此类将 URI 的字符串值作为普通 html 表单字段的值(而不是 URI 值)。此类不应用于解码完整的 URI/URL 值,因为它不是为此目的而设计的)

来自java docs of URLDecoder#decode(String)

解码一个 x-www-form-urlencoded 字符串。平台默认 编码用于确定任何字符代表什么字符 “%xy”形式的连续序列。

希望对你有帮助。

基于 cmets 更新 #1:

根据section 2.2,如果 URI 组件的数据与保留字符发生冲突,则必须在形成 URI 之前对冲突数据进行百分比编码。

URI 的不同部分根据其上下文具有不同的保留字集,这一点也很重要。例如,/ 符号仅保留在 URI 的路径部分,+ 符号保留在查询字符串部分。所以没有必要在 查询部分 中转义 /,同样也不需要在 路径部分 中转义 +

在您的示例中,URI 生产者 File.toURI 不编码 + 登录 URI 的路径部分(因为 +' is not considered as reserved word in path part) and you see the +' 登录到 URI 的字符串表示形式。

您可以参考URI recommendation了解更多详情。

相关答案:

  1. https://stackoverflow.com/a/1006074/1700467
  2. https://stackoverflow.com/a/2678602/1700467
  3. https://stackoverflow.com/a/4571518/1700467

【讨论】:

  • 我的问题是 new File("hello+world.txt").toURI() 没有转义 URI 中的 +,因为在转换为 URI 时需要对文件名中的加号进行编码以保留它。
  • @adamretter 但这正是他的意思,+ 字符不必进行编码。阅读例如URI specification BNF。那里的+ 有点特殊,但这只是因为它在search 中具有特殊含义,并且仅在path 中有效。不过就是这样,这里的+path的一部分,它是有效的,不需要编码。 (你会包括例如? 会有所不同)
  • 我不同意。 RFC 3986 第 2.2 节清楚地表明 + 是 URI 中的保留字符:tools.ietf.org/html/rfc3986#section-2.2。特别是我从 RFC 中引用:1)“在用相应的百分比编码八位字节替换保留字符方面不同的 URI 是不等价的。”,以及 2)“生成 URI 的应用程序应该对对应的数据八位字节进行百分比编码保留集中的字符”。在这种情况下,我认为 File.toURI 是一个 URI 生成应用程序。
  • 感谢您的全面回答。但是,我仍然不相信 + 不应该被编码。虽然在 ABNF 中为 URI tools.ietf.org/html/rfc3986#appendix-A 定义的 URI 的 pchar 部分中允许使用 +,但百分比编码字符也是如此,但 ABNF 表示编码形式。正如规范在第 2.4 节中所说:“一旦生成,URI 始终采用百分比编码形式。”即 URI 始终采用编码形式,如果我没记错的话,URI 路径中的 + 表示空格字符而不是 + 字符。 ABNF 以及第 2.2 和 2.4 节似乎证实了这一点?
  • 啊!!!好的,所以重新阅读了您更新的答案并看了几次stackoverflow.com/a/4571518/1700467 我想我现在明白了。这里的问题实际上是URLDecoder,因为它是为非常特定的 HTML 用例而设计的。感谢您的出色回答。
【解决方案2】:

我假设,您想将+ 编码为%2B。因此,当您将其解码回来时,您会以+ 符号的形式将其取回。

如果是这种情况,那么你需要使用URLEncoder.encode

System.out.println(URLEncoder.encode(new File("hello+world.txt").toURI().toString()));

它将编码所有特殊字符,包括+ 符号。输出将是

file%3A%2Fhome%2FT8hvs7%2Fhello%2Bworld.txt

现在,解码使用URLDecoder.decode

System.out.println(URLDecoder.decode("file%3A%2Fhome%2FwQCXni%2Fhello%2Bworld.txt"));

会显示

file:/home/wQCXni/hello+world.txt

【讨论】:

    【解决方案3】:

    显然这不是错误,documentation 明确表示

    The plus sign "+" is converted into a space character " " .
    

    你可以这样做:https://ideone.com/JHDkM4

    import java.util.*;
    import java.lang.*;
    import java.io.*;
    import static java.lang.System.out;
    
    
    class Ideone
    {
        public static void main (String[] args) throws java.lang.Exception
        {
            out.println(new File("hello+world.txt").toURI().toString());
            out.println(java.net.URLDecoder.decode(new File("hello+world.txt").toURI().toURL().toString()));
            out.println(new File("hello+world.txt").toURI().toString().replaceAll("\\+", "%2B"));
        }
    }
    

    【讨论】:

    • 我的问题是new File("hello+world.txt").toURI() 没有转义 URI 中的 +,因为在转换为 URI 时需要对文件名中的加号进行编码以保留它。
    【解决方案4】:

    如果 URI 表示文件,则让 File 类解码 URI。

    假设我们有一个文件的 URI,例如获取 jar 文件的文件路径: URI uri = MyClass.class.getProtectionDomain().getCodeSource().getLocation().toURI();

    System.out.println(uri.toString());
    => BAD : 将显示加号,但 %20 表示空格

    System.out.println(URLDecoder.decode(uri.toString(), StandardCharsets.UTF_8.toString()));
    => BAD : 将显示空格而不是 %20,但也会显示加号

    System.out.println(new File(uri).getAbsolutePath());
    => 好

    【讨论】:

      【解决方案5】:

      尝试使用反斜杠 \ 转义加号 也一样

      new File("hello\+world.txt").toURI().toString()
      

      【讨论】:

      • 那甚至无法编译!
      • 如果你正确使用它,它的代码只是在它编译和工作的字符串中带有一个 \
      • 导入 java.io.File;公共类测试 { public static void main(String args[]) { new File("hello\+world.txt").toURI().toString(); } } javac Test.java Test.java:5: 错误:非法转义字符
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      • 1970-01-01
      • 2010-11-11
      • 2015-10-18
      • 1970-01-01
      • 1970-01-01
      • 2011-08-27
      相关资源
      最近更新 更多