URLEncoder无法转换空格字符

Che*_*eng 166 java url urlencode

我期待着

System.out.println(java.net.URLEncoder.encode("Hello World", "UTF-8"));
Run Code Online (Sandbox Code Playgroud)

输出:

Hello%20World

(20是空格的ASCII十六进制代码)

但是,我得到的是:

Hello+World

我使用了错误的方法吗?我应该使用的正确方法是什么?

dog*_*ane 215

这表现得如预期.该URLEncoder工具对于如何在HTML表单的URL编码的HTML规范.

来自javadocs:

此类包含用于将String转换为application/x-www-form-urlencoded MIME格式的静态方法.

并从HTML规范:

应用程序/ x-WWW窗体-urlencoded

使用此内容类型提交的表单必须按如下方式编码:

  1. 控制名称和值将被转义.空格字符替换为"+"

您将不得不更换它,例如:

System.out.println(java.net.URLEncoder.encode("Hello World", "UTF-8").replace("+", "%20"));
Run Code Online (Sandbox Code Playgroud)

  • @congliu这是不正确的 - 您可能正在考虑使用regex的replaceAll() - replace()是简单的字符序列替换. (25认同)
  • 那确实是一个答案,而不是替换不存在java库或执行任务的函数/? (18认同)
  • 是的@congliu好方法是:URLEncoder.encode("Myurl","utf-8").replaceAll("\\ +","%20"); (11认同)
  • @ClintEastwood这个答案鼓励使用java.net.URLEncoder,它不是最初要求的工作.所以这个答案建议一个补丁,使用replace(),在它上面.为什么不?因为这个解决方案容易出错,并且可能导致其他20个类似的问题但具有不同的特征.这就是为什么我说这是短视的原因. (9认同)
  • 加号需要转义为`t.println(java.net.URLEncoder.encode("Hello World","UTF-8").replace("\\ +","%20"));` (4认同)
  • 因为像这样的短视解决方案是危险的,所以被低估了.这不仅仅是关于空间字符,请参阅[RFC 3986](https://tools.ietf.org/html/rfc3986)关于URL编码. (3认同)
  • @eis 正如提问者所注意到的,“URLEncoder.encode”并没有满足提问者的需要。此答案中发布的代码是通过调用“String.replace”来修补它。这是很弱的(仅适用于空格字符)并且过于复杂:只需使用正确的编码即可。例如,参见 /sf/answers/2211652551/ 打个比方,如果答案是“为什么 `2+2` 不给出 5?”,这就像建议“只做 `2+2” +1`,你会得到 5"。 (3认同)
  • @pyb我希望我可以对你的评论进行投票.问题是关于空间特征......为什么一切都必须概括?同样,如果用户希望将*all*出现的加号替换为'%20`,这个答案将不会100%精确,因为他们需要使用`String#replaceAll(regex,replacement)`,这种情况``\\ +"`将是强制性的,但这个答案再次有效地回答了@dogbane所提出的确切问题. (2认同)
  • @pyb令我感到难过的是,执行一次replace()调用被认为比添加像guava这样的功能强大的库更为复杂,这确实给软件带来了更多复杂性。我的观点是,仅替换空格字符是否存在相关问题?我还没有看到任何实际示例,其中两种编码之间的差异会导致问题。据我所知,其他区别是[此处列出](https://www.leveluplunch.com/java/examples/encode-url-string/)的字符,如果需要,可以将它们添加到替换列表中。 (2认同)

pyb*_*pyb 49

空间%20在URL中以及+在表单提交的数据中编码(内容类型application/x-www-form-urlencoded).你需要前者.

使用番石榴:

dependencies {
     compile 'com.google.guava:guava:23.0'
     // or, for Android:
     compile 'com.google.guava:guava:23.0-android'
}
Run Code Online (Sandbox Code Playgroud)

您可以使用UrlEscapers:

String encodedString = UrlEscapers.urlFragmentEscaper().escape(inputString);
Run Code Online (Sandbox Code Playgroud)

不要使用String.replace,这只会编码空间.请改用库.


axt*_*avt 25

这个类执行application/x-www-form-urlencoded型编码,而不是编码%的,因此更换 with +是一个正确的行为.

来自javadoc:

编码String时,以下规则适用:

  • 字母数字字符"a"到"z","A"到"Z"和"0"到"9"保持不变.
  • 特殊字符"."," - ","*"和"_"保持不变.
  • 空格字符""被转换为加号"+".
  • 所有其他字符都是不安全的,并且首先使用某种编码方案将其转换为一个或多个字节.然后每个字节由3个字符的字符串"%xy"表示,其中xy是字节的两位十六进制表示.建议使用的编码方案是UTF-8.但是,出于兼容性原因,如果未指定编码,则使用平台的默认编码.

  • @Stallman 这是 Java,而不是 JavaScript。完全不同的语言。 (2认同)

fmu*_*car 17

编码查询参数

org.apache.commons.httpclient.util.URIUtil
    URIUtil.encodeQuery(input);
Run Code Online (Sandbox Code Playgroud)

或者如果你想在URI中转义字符

public static String escapeURIPathParam(String input) {
  StringBuilder resultStr = new StringBuilder();
  for (char ch : input.toCharArray()) {
   if (isUnsafe(ch)) {
    resultStr.append('%');
    resultStr.append(toHex(ch / 16));
    resultStr.append(toHex(ch % 16));
   } else{
    resultStr.append(ch);
   }
  }
  return resultStr.toString();
 }

 private static char toHex(int ch) {
  return (char) (ch < 10 ? '0' + ch : 'A' + ch - 10);
 }

 private static boolean isUnsafe(char ch) {
  if (ch > 128 || ch < 0)
   return true;
  return " %$&+,/:;=?@<>#%".indexOf(ch) >= 0;
 }
Run Code Online (Sandbox Code Playgroud)

  • 使用`org.apache.commons.httpclient.util.URIUtil`似乎是解决此问题的最有效方法! (2认同)
  • URIUtil 在当前版本中似乎已经消失,有替代方案吗? (2认同)

McD*_*ell 11

Hello+World是浏览器如何application/x-www-form-urlencodedGET请求编码表单数据(),这是URI的查询部分的普遍接受的形式.

http://host/path/?message=Hello+World
Run Code Online (Sandbox Code Playgroud)

如果将此请求发送到Java servlet,则servlet将正确解码参数值.通常这里唯一出现问题的是编码不匹配.

严格地说,HTTP或URI规范中不要求使用application/x-www-form-urlencoded键值对来编码查询部分; 查询部分只需要是Web服务器接受的形式.实际上,这不太可能是一个问题.

将此编码用于URI的其他部分(例如路径)通常是不正确的.在这种情况下,您应该使用RFC 3986中描述的编码方案.

http://host/Hello%20World
Run Code Online (Sandbox Code Playgroud)

更多这里.


MrT*_*Tux 7

如果你想对URI路径组件进行编码,你也可以使用标准的JDK函数,例如

public static String encodeURLPathComponent(String path) {
    try {
        return new URI(null, null, path, null).toASCIIString();
    } catch (URISyntaxException e) {
        // do some error handling
    }
    return "";
}
Run Code Online (Sandbox Code Playgroud)

URI 类还可用于对 URI 的不同部分或整个 URI 进行编码。

更新:我刚刚意识到,如果路径中的斜杠之前有冒号,或者冒号之前的部分不是有效的 URI 方案,则此方法不起作用。此外,它将所有 unicode 标准化为 NFC。

  • 谢谢!这对我有用。是的,有点迂回,但比将 Guava 拉到我的小项目中来实现这一功能要好。 (2认同)

Ben*_*ema 6

其他答案要么是手动替换字符串,要么是实际上为HTML格式编码的URLEncoder,要么是Apache 放弃的 URIUtil,或者是使用Guava的UrlEscapers。最后一个很好,但是它不提供解码器。

Apache Commons Lang提供URLCodec,该URLCodec根据URL格式rfc3986进行编码解码。

String encoded = new URLCodec().encode(str);
String decoded = new URLCodec().decode(str);
Run Code Online (Sandbox Code Playgroud)

如果您已经在使用Spring,则还可以选择使用 UriUtils类。

  • URLCodec在这里不是一个很好的解决方案,因为它会将空格编码为加号,但是问题是要求将空格编码为%20。 (4认同)
  • Spring 的 UriUtil.encodeQuery (用于查询字符串)对我有用。 (2认同)

LeO*_*LeO 6

虽然很老了,但反应很快:

Spring 提供了 UriUtils - 通过它你可以指定如何编码以及它与 URI 的哪一部分相关,例如

encodePathSegment
encodePort
encodeFragment
encodeUriVariables
....
Run Code Online (Sandbox Code Playgroud)

我使用它们是因为我们已经使用了 Spring,即不需要额外的库!


Chr*_*pix 5

刚刚在 Android 上也为此苦苦挣扎,设法偶然发现了 Uri.encode(String, String) 而特定于 android (android.net.Uri) 可能对某些人有用。

静态字符串编码(字符串 s,字符串允许)

https://developer.android.com/reference/android/net/Uri.html#encode(java.lang.String, java.lang.String)


tch*_*dyk 5

这不是单行代码,但您可以使用:

URL url = new URL("https://some-host.net/dav/files/selling_Rosetta Stone Case Study.png.aes");
URI uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(), url.getQuery(), url.getRef());
System.out.println(uri.toString());
Run Code Online (Sandbox Code Playgroud)

这会给你一个输出:

https://some-host.net/dav/files/selling_Rosetta%20Stone%20Case%20Study.png.aes
Run Code Online (Sandbox Code Playgroud)