URLEncoder无法翻译空格字符


187

我期待

System.out.println(java.net.URLEncoder.encode("Hello World", "UTF-8"));

输出:

Hello%20World

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

但是,我得到的是:

Hello+World

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


3
类名确实令人困惑,许多人错误地使用了它。但是他们没有注意到,因为当应用URLDecoder时,将恢复原始值,因此+或%20对他们而言并不重要。
2011年

Answers:


231

这表现出预期。该URLEncoder工具实现了有关如何以HTML形式编码URL的HTML规范。

javadocs

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

并来自HTML规范

应用程序/ x-www-form-urlencoded

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

  1. 控件名称和值被转义。空格字符替换为“ +”

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

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

19
好吧,这确实是一个答案,而不是取代不是没有Java库或函数来执行任务/吗?
co2f2e

5
加号必须被忽略t.println(java.net.URLEncoder.encode("Hello World", "UTF-8").replace("\\+", "%20"));
乔治

26
@congliu是错误的-您可能正在考虑与regex一起使用的replaceAll()-replace()是简单的字符序列替换。
CupawnTae

13
是@congliu的好方法是:URLEncoder.encode(“ Myurl”,“ utf-8”)。replaceAll(“ \\ +”,“%20”);

11
@ClintEastwood这个答案鼓励使用java.net.URLEncoder,而不是最初要求的工作。因此,此答案提出了一个在其顶部使用replace()的补丁程序。为什么不?因为此解决方案易于出错,并且可能导致其他20个类似问题,但具有不同的特征。这就是为什么我说这是近视的。
pyb

59

%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'
}

您可以使用UrlEscapers

String encodedString = UrlEscapers.urlFragmentEscaper().escape(inputString);

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


它也适用于Android com.google.guava:guava:22.0-rc1-android。
Bevor

1
@Bevor rc1表示1st Release Candidate,即尚未批准发布的版本。如果可以,请选择没有快照,alpha,beta,rc的版本,因为它们已知包含错误。
pyb

1
@pyb谢谢,但是无论如何我将在项目完成后更新库。意味着,如果没有最终版本,我将不会继续生产。而且它仍然需要花费数周的时间,因此我想会有一个最终版本。
Bevor

1
不幸的是,与Apache的URLCodec不同,Guava没有提供解码器。
Benny Bottema,

26

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

从javadoc:

编码字符串时,以下规则适用:

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

@axtavt很好的解释。但是我仍然有一些问题。在中url,空格应解释为%20。那么我们需要做url.replaceAll("\\+", "%20")什么?如果是javascript,则不应使用escapefunction。使用encodeURIencodeURIComponent代替。那正是我所想。
Alston 2014年

1
@Stallman,这是Java,而不是JavaScript。完全不同的语言。
查尔斯伍德

19

编码查询参数

org.apache.commons.httpclient.util.URIUtil
    URIUtil.encodeQuery(input);

或者,如果您想在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;
 }

3
使用org.apache.commons.httpclient.util.URIUtil似乎是解决问题的最有效方法!
斯特凡阿马尔

URIUtil在当前版本中似乎已消失,还有其他选择吗?
wutzebaer

11

Hello+World这是浏览器如何对请求的表单数据(application/x-www-form-urlencoded)进行编码GET,这是URI查询部分通常接受的表单。

http://host/path/?message=Hello+World

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

严格来说,HTTP或URI规范中没有要求要使用application/x-www-form-urlencoded键值对对查询部分进行编码。查询部分仅需要采用Web服务器接受的形式。实际上,这不太可能成为问题。

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

http://host/Hello%20World

这里更多。


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

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


6
URLCodec并不是一个很好的解决方案,因为它会将空格编码为加号,但是问题是要求将空格编码为%20。
davidwebster48 '18


3

“ +”是正确的。如果您确实需要%2​​0,则随后自己更换Plusses。


5
如果初始字符串确实包含一个+字符,则可能会有问题。
Alexis Dufrenoy

17
@Traroth-并非如此。一个+在原来的文本字符应该被编码为%2B
泰德·霍普

说这+是正确的,至少不了解上下文至少是学问的。不赞成投票。阅读其他答案以了解何时使用+或%20。
克林特·伊斯特伍德

@ClintEastwood:您能告诉我任何用例,因为URL中空格的+字符不正确吗?除非另一端有不合格的URL解析器?
丹尼尔(Daniel)

@Daniel当然可以,不是说“不正确”但不合适吗?是。分析工具通常使用查询参数,其值由某个字符分隔,例如“ +”。在这种情况下,使用“ +”代替“%20”将是错误的。“ +”用于以某种形式转义空格,而“百分比编码”(又称URL编码)更适合于URL。
克林特·伊斯特伍德

2

这对我有用

org.apache.catalina.util.URLEncoder ul = new org.apache.catalina.util.URLEncoder().encode("MY URL");

2

尽管已经很老了,但还是可以快速做出反应:

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

encodePathSegment
encodePort
encodeFragment
encodeUriVariables
....

我使用它们是因为我们已经在使用Spring,即不需要附加库!


Spring中还有其他可以进行URL编码的内容吗?我问是因为,当我使用getForObject(的一部分RestTemplate)URL发出测试请求时,它写出的逗号未编码,但对UriUtils.encode(...)逗号进行了编码,这意味着MockRestServiceServer如果我使用的输出,则我的路径将不匹配UriUtils.encode
IpsRich

我认为这回答了我的问题:stackoverflow.com/a/20885702/1999993
IpsRich


0

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

是的,此方法java.net.URLEncoder.encode并非用于根据规范将“”转换为“ 20%”()。

空格字符“”转换为加号“ +”。

即使这不是正确的方法,您也可以将其修改为:System.out.println(java.net.URLEncoder.encode("Hello World", "UTF-8").replaceAll("\\+", "%20"));有一个美好的一天=)。


您建议使用一种不适当的方法(URLEncoder.encode)并使用该方法进行修补,replaceAll该方法仅在此特定情况下有效。请改用正确的类和方法,请参阅其他答案。
pyb

@pyb看来您听不懂我写的内容。我从来没有说过“我建议使用它”,而是说“可以”。在编写之前,请阅读并理解。
Pregunton

这是一个问答网站,而不是人们聊天的常规留言板。如果您有其他意见,请使用这些意见。更长的谈话?使用聊天。不要发布您不同意的代码作为答案。在贡献和讲授他人之前,请阅读并理解本网站的规则。
pyb

1
我支持它,因为大多数其他解决方案都提供相同的建议。没有提供“特定情况”来证明此方法是错误的。对于尝试使用replaceAll进行有效修补的方法,将apache commons与try-catch块或依赖项结合使用非常麻烦。
Eugene Kartoyev '18

0

如果您使用码头,那么org.eclipse.jetty.util.URIUtil将解决问题。

String encoded_string = URIUtil.encodePath(not_encoded_string).toString();

-2

使用MyUrlEncode.URLencoding(String url,String enc)处理问题

    public class MyUrlEncode {
    static BitSet dontNeedEncoding = null;
    static final int caseDiff = ('a' - 'A');
    static {
        dontNeedEncoding = new BitSet(256);
        int i;
        for (i = 'a'; i <= 'z'; i++) {
            dontNeedEncoding.set(i);
        }
        for (i = 'A'; i <= 'Z'; i++) {
            dontNeedEncoding.set(i);
        }
        for (i = '0'; i <= '9'; i++) {
            dontNeedEncoding.set(i);
        }
        dontNeedEncoding.set('-');
        dontNeedEncoding.set('_');
        dontNeedEncoding.set('.');
        dontNeedEncoding.set('*');
        dontNeedEncoding.set('&');
        dontNeedEncoding.set('=');
    }
    public static String char2Unicode(char c) {
        if(dontNeedEncoding.get(c)) {
            return String.valueOf(c);
        }
        StringBuffer resultBuffer = new StringBuffer();
        resultBuffer.append("%");
        char ch = Character.forDigit((c >> 4) & 0xF, 16);
            if (Character.isLetter(ch)) {
            ch -= caseDiff;
        }
        resultBuffer.append(ch);
            ch = Character.forDigit(c & 0xF, 16);
            if (Character.isLetter(ch)) {
            ch -= caseDiff;
        }
         resultBuffer.append(ch);
        return resultBuffer.toString();
    }
    private static String URLEncoding(String url,String enc) throws UnsupportedEncodingException {
        StringBuffer stringBuffer = new StringBuffer();
        if(!dontNeedEncoding.get('/')) {
            dontNeedEncoding.set('/');
        }
        if(!dontNeedEncoding.get(':')) {
            dontNeedEncoding.set(':');
        }
        byte [] buff = url.getBytes(enc);
        for (int i = 0; i < buff.length; i++) {
            stringBuffer.append(char2Unicode((char)buff[i]));
        }
        return stringBuffer.toString();
    }
    private static String URIEncoding(String uri , String enc) throws UnsupportedEncodingException { //对请求参数进行编码
        StringBuffer stringBuffer = new StringBuffer();
        if(dontNeedEncoding.get('/')) {
            dontNeedEncoding.clear('/');
        }
        if(dontNeedEncoding.get(':')) {
            dontNeedEncoding.clear(':');
        }
        byte [] buff = uri.getBytes(enc);
        for (int i = 0; i < buff.length; i++) {
            stringBuffer.append(char2Unicode((char)buff[i]));
        }
        return stringBuffer.toString();
    }

    public static String URLencoding(String url , String enc) throws UnsupportedEncodingException {
        int index = url.indexOf('?');
        StringBuffer result = new StringBuffer();
        if(index == -1) {
            result.append(URLEncoding(url, enc));
        }else {
            result.append(URLEncoding(url.substring(0 , index),enc));
            result.append("?");
            result.append(URIEncoding(url.substring(index+1),enc));
        }
        return result.toString();
    }

}

9
重新发明轮子,在代码库中添加易于出错的超级代码几乎总是一个错误的决定。
克林特·伊斯特伍德

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.