공백 문자를 번역 할 수없는 URLEncoder


179

나는 기대하고있다

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

출력 :

Hello%20World

(20은 공백을위한 ASCII 16 진 코드입니다)

그러나 내가 얻는 것은 다음과 같습니다.

Hello+World

잘못된 방법을 사용하고 있습니까? 사용해야하는 올바른 방법은 무엇입니까?


3
클래스 이름은 실제로 혼란스럽고 많은 사람들이 그것을 잘못 사용했습니다. 그러나 URLDecoder를 적용하면 원래 값이 복원되므로 + 또는 % 20은 실제로 중요하지 않기 때문에 인식하지 못합니다.
확실한

답변:


227

이것은 예상대로 동작합니다. URLEncoder구현 HTML 양식에서 어떻게 인코딩 된 URL에 대한 HTML 사양.

로부터 의 javadoc :

이 클래스에는 문자열을 application / x-www-form-urlencoded MIME 형식으로 변환하기위한 정적 메소드가 포함되어 있습니다.

그리고 HTML 사양에서 :

application / 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"));
George

26
@congliu 맞지 않습니다-아마 regex와 함께 작동하는 replaceAll ()을 생각하고 있습니다-replace ()는 간단한 문자 시퀀스 교체입니다.
CupawnTae

12
네 @congliu 좋은 방법은 다음과 같습니다 : URLEncoder.encode ( "Myurl", "utf-8"). replaceAll ( "\\ +", "% 20");
eento

9
@ClintEastwood이 답변은 원래 요청 된 작업을 수행하지 않는 java.net.URLEncoder의 사용을 권장합니다. 그리고이 답변은 replace ()를 사용하여 패치를 제안합니다. 왜 안돼? 이 솔루션은 버그가 발생하기 쉬우므로 20 가지의 다른 유사한 질문을 야기 할 수 있지만 다른 특성을 가질 수 있습니다. 이것이 내가 근시안이라고 말한 이유입니다.
pyb

57

공백은 %20URL 로 인코딩 되고+ 제출 된 데이터 (콘텐츠 유형 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은 첫 번째 릴리스 후보, 즉 아직 일반 릴리스 용으로 승인되지 않은 버전을 의미합니다. 가능하면 버그가있는 것으로 알려진 스냅 샷, 알파, 베타, rc가없는 버전을 선택하십시오.
pyb

1
@pyb 감사하지만 프로젝트가 완료되면 어쨌든 libs를 업데이트 할 것입니다. 의미, 나는 최종 버전없이 자극에 가지 않을 것입니다. 그리고 여전히 많은 시간이 걸리므로 최종 버전이 있다고 생각합니다.
Bevor

1
불행히도 구아바는 Apache의 URLCodec 와 달리 디코더를 제공하지 않습니다 .
베니 보 테마

26

이 클래스 application/x-www-form-urlencoded는 퍼센트 인코딩이 아닌 -type 인코딩을 수행 하므로으로 대체 하는 +것이 올바른 동작입니다.

javadoc에서 :

문자열을 인코딩 할 때 다음 규칙이 적용됩니다.

  • 영숫자 문자 "a"~ "z", "A"~ "Z"및 "0"~ ​​"9"는 동일하게 유지됩니다.
  • 특수 문자 ".", "-", "*"및 "_"는 동일하게 유지됩니다.
  • 공백 문자 ""는 더하기 부호 "+"로 변환됩니다.
  • 다른 모든 문자는 안전하지 않으며 일부 인코딩 체계를 사용하여 먼저 하나 이상의 바이트로 변환됩니다. 그런 다음 각 바이트는 3 자 문자열 "% xy"로 표시됩니다. 여기서 xy는 바이트의 두 자리 16 진수 표현입니다. 권장되는 인코딩 체계는 UTF-8입니다. 그러나 호환성을 위해 인코딩을 지정하지 않으면 플랫폼의 기본 인코딩이 사용됩니다.

@axtavt 좋은 설명입니다. 그러나 여전히 몇 가지 질문이 있습니다. 에서 url의 공간으로 해석되어야한다 %20. 그래서 우리는해야합니까 url.replaceAll("\\+", "%20")? 그리고 그것이 자바 스크립트라면, escape함수를 사용해서는 안됩니다 . encodeURI또는 encodeURIComponent대신 사용하십시오 . 그것이 내가 생각했던 거죠.
Alston

1
@Stallman 이것은 JavaScript가 아닌 Java입니다. 완전히 다른 언어.
찰스 우드

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하는 것이 문제를 해결하는 가장 효율적인 방법 인 것 같습니다!
Stéphane Ammar

11

Hello+World브라우저가 요청에 application/x-www-form-urlencoded대한 양식 데이터 ( )를 인코딩하는 방법 GET이며 이는 URI의 쿼리 부분에 일반적으로 허용되는 양식입니다.

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

이 요청을 Java 서블릿으로 보낸 경우 서블릿은 매개 변수 값을 올바르게 디코딩합니다. 일반적으로 여기에 문제가있는 유일한 시간은 인코딩이 일치하지 않는 경우입니다.

엄밀히 말하면 HTTP 또는 URI 사양에는 쿼리 부분을 사용하여 인코딩해야 할 요구 사항이 없습니다. application/x-www-form-urlencoded 키-값 쌍을 . 쿼리 부분은 웹 서버가 허용하는 형식이어야합니다. 실제로 이것은 문제가되지 않을 것입니다.

URI의 다른 부분 (예 : 경로)에이 인코딩을 사용하는 것은 일반적으로 올바르지 않습니다. 이 경우 RFC 3986에 설명 된 대로 인코딩 체계를 사용해야합니다 .

http://host/Hello%20World

여기에 .


5

다른 답변은 수동 문자열 대체, 실제로 HTML 형식으로 인코딩하는 URLEncoder , Apache의 버려진 URIUtil 또는 Guava의 UrlEscapers를 사용하는 것 입니다. 마지막은 디코더를 제공하지 않는 한 괜찮습니다.

Apache Commons Lang은 URL 형식 rfc3986 에 따라 인코딩 하고 디코딩 하는 URLCodec을 제공합니다 .

String encoded = new URLCodec().encode(str);
String decoded = new URLCodec().decode(str);

이미 Spring을 사용 하고 있다면 UriUtils 클래스 를 사용하도록 선택할 수도 있습니다 .


6
URLCodec은 공백을 플러스로 인코딩하기 때문에 여기에서 좋은 해결책은 아니지만 질문은 공백을 % 20으로 인코딩하도록 요청합니다.
davidwebster48

3

"+"가 맞습니다. % 20이 정말로 필요하다면, 나중에 Plusses를 교체하십시오.


5
초기 문자열에 실제로 + 문자가 포함 된 경우 문제가있을 수 있습니다.
Alexis Dufrenoy

17
@Traroth-실제로는 아닙니다. +원래 텍스트 문자로 인코딩 할 예정이다 %2B.
테드 홉

+상황을 알지 못하고 옳다고 말하는 것은 적어도 pedantic이다. 공감. + 또는 % 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");

1

그럼에도 불구하고 빠른 응답 :

Spring은 UriUtils를 제공합니다.이를 통해 인코딩 방법과 URI와 관련된 부분을 지정할 수 있습니다.

encodePathSegment
encodePort
encodeFragment
encodeUriVariables
....

나는 이미 Spring을 사용하고 있기 때문에 그것들을 사용합니다. 즉, 추가 라이브러리가 필요하지 않습니다!



0

잘못된 방법을 사용하고 있습니까? 사용해야하는 올바른 방법은 무엇입니까?

예,이 메소드 java.net.URLEncoder.encode는 spec ( source ) 에 따라 ""를 "20 %"로 변환하기 위해 만들어지지 않았습니다 .

공백 문자 ""는 더하기 부호 "+"로 변환됩니다.

이것이 올바른 방법이 아니더라도, System.out.println(java.net.URLEncoder.encode("Hello World", "UTF-8").replaceAll("\\+", "%20"));좋은 하루 되세요 =).


적절하지 않은 방법 ( URLEncoder.encode)을 사용 replaceAll하고이 특정 경우에만 작동 하는 방법으로 패치 하는 것이 좋습니다. 대신 올바른 클래스와 방법을 사용하십시오. 다른 답변을 참조하십시오.
pyb

@pyb는 내가 작성한 것을 이해할 수없는 것처럼 보입니다. 나는 "나는 그것을 사용하는 것이 좋습니다"라고 말한 적이 없습니다. 쓰기 전에 읽고 이해하십시오.
Pregunton

사람들이 채팅하는 일반 게시판이 아니라 질문과 답변 웹 사이트입니다. 측면 의견이 있으면 의견을 사용하십시오. 더 긴 이야기? 채팅을 사용하십시오. 동의하지 않는 코드는 답변으로 게시하지 마십시오. 다른 사람을 기고하고 강의하기 전에이 사이트의 규칙을 읽고 이해하십시오.
pyb

1
대부분의 다른 솔루션이 동일한 조언을 제공하기 때문에이를 철회하고 있습니다. 이 방법이 잘못되었음을 증명하기 위해 "특정 사례"가 제공되지 않았습니다. try-catch 블록 또는 종속성과 함께 아파치 커먼을 사용하는 것은 replaceAll로 효과적으로 패치 할 수있는 방법에 너무 번거 롭습니다.
유진 카르 토 예프

-2

사용 MyUrlEncode.URLencoding (문자열 URL, 문자열 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
오류를 발생시키기 쉬운 코드를 코드베이스에 추가하는 것은 거의 항상 나쁜 결정입니다.
클린트 이스트우드

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.