URLEncoder không thể dịch ký tự khoảng trắng


179

tôi đang mong đợi

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

Đến đầu ra:

Hello%20World

(20 là mã Hex ASCII cho không gian)

Tuy nhiên, những gì tôi nhận được là:

Hello+World

Tôi đang sử dụng phương pháp sai? Phương pháp chính xác tôi nên sử dụng là gì?


3
Tên lớp thật sự khó hiểu và nhiều người đã sử dụng sai. tuy nhiên họ không chú ý đến nó, vì khi URLDecoder được áp dụng, giá trị ban đầu được khôi phục, vì vậy + hoặc% 20 không thực sự quan trọng đối với họ.
chối cãi

Câu trả lời:


227

Điều này hành xử như mong đợi. Việc URLEncodertriển khai Thông số kỹ thuật HTML để biết cách mã hóa URL dưới dạng HTML.

Từ javadocs :

Lớp này chứa các phương thức tĩnh để chuyển đổi Chuỗi thành định dạng MIME của ứng dụng / x-www-form-urlencoding.

và từ Đặc tả HTML :

application / x-www-form-urlencoding

Các biểu mẫu được gửi với loại nội dung này phải được mã hóa như sau:

  1. Tên và giá trị kiểm soát được thoát. Các ký tự khoảng trắng được thay thế bằng `+ '

Bạn sẽ phải thay thế nó, vd:

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

19
Vâng Đây thực sự là một câu trả lời, thay vì không có thư viện java hoặc chức năng để thực hiện nhiệm vụ /?
co2f2e

5
Dấu cộng cần được thoátt.println(java.net.URLEncoder.encode("Hello World", "UTF-8").replace("\\+", "%20"));
George

26
@congliu điều đó không chính xác - có lẽ bạn đang nghĩ đến thay thế All () hoạt động với regex - thay thế () là thay thế chuỗi ký tự đơn giản.
CupawnTae

12
Có @congliu cách tốt là: URLEncoder.encode ("Myurl", "utf-8"). ThayTất cả ("\\ +", "% 20");
eento

9
@ClintEastwood Câu trả lời này khuyến khích việc sử dụng java.net.URLEncoder không phải là công việc của những gì được hỏi ban đầu. Và vì vậy, câu trả lời này gợi ý một bản vá, sử dụng thay thế (), trên đầu trang của nó. Tại sao không? Bởi vì giải pháp này dễ bị lỗi và có thể dẫn đến 20 câu hỏi tương tự khác nhưng với một nhân vật khác. Đó là lý do tại sao tôi nói điều này là thiển cận.
pyb

57

Một không gian được mã hóa thành %20URL và +dưới dạng dữ liệu được gửi (ứng dụng loại nội dung / x-www-form-urlencoding). Bạn cần cái trước.

Sử dụng ổi :

dependencies {
     compile 'com.google.guava:guava:23.0'
     // or, for Android:
     compile 'com.google.guava:guava:23.0-android'
}

Bạn có thể sử dụng UrlEscapers :

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

Đừng sử dụng String.replace, điều này sẽ chỉ mã hóa không gian. Sử dụng một thư viện thay thế.


Nó cũng hoạt động cho Android, com.google.guava: guava: 22.0-rc1-android.
Bevor

1
@Bevor RC1 có nghĩa là Ứng viên phát hành đầu tiên, tức là một phiên bản chưa được phê duyệt để phát hành chung. Nếu bạn có thể, hãy chọn một phiên bản không có ảnh chụp nhanh, alpha, beta, RC vì chúng được biết là có lỗi.
pyb

1
@pyb Cảm ơn, nhưng dù sao tôi cũng sẽ cập nhật libs khi dự án của tôi kết thúc. Có nghĩa là, tôi sẽ không đến prod mà không có phiên bản cuối cùng. Và nó vẫn còn mất nhiều tuần, vì vậy tôi đoán đã có phiên bản cuối cùng rồi.
Bevor

1
Thật không may, Guava không cung cấp bộ giải mã, không giống như URLCodec của Apache .
Benny Bottema

26

Lớp này thực hiện application/x-www-form-urlencodedmã hóa -type thay vì mã hóa phần trăm, do đó thay thế bằng +là một hành vi đúng.

Từ javadoc:

Khi mã hóa Chuỗi, các quy tắc sau sẽ được áp dụng:

  • Các ký tự chữ và số "a" đến "z", "A" đến "Z" và "0" đến "9" vẫn giữ nguyên.
  • Các ký tự đặc biệt ".", "-", "*" và "_" vẫn giữ nguyên.
  • Ký tự khoảng trắng "" được chuyển đổi thành dấu cộng "+".
  • Tất cả các ký tự khác đều không an toàn và trước tiên được chuyển đổi thành một hoặc nhiều byte bằng cách sử dụng một số lược đồ mã hóa. Sau đó, mỗi byte được biểu thị bằng chuỗi 3 ký tự "% xy", trong đó xy là biểu diễn thập lục phân hai chữ số của byte. Lược đồ mã hóa được khuyến nghị sử dụng là UTF-8. Tuy nhiên, vì lý do tương thích, nếu mã hóa không được chỉ định, thì mã hóa mặc định của nền tảng được sử dụng.

@axtavt Giải thích hay. Nhưng tôi vẫn có một số câu hỏi. Trong url, không gian nên được hiểu là %20. Vậy chúng ta cần phải làm gì url.replaceAll("\\+", "%20")? Và nếu đó là javascript, chúng ta không nên sử dụng escapechức năng. Sử dụng encodeURIhoặc encodeURIComponentthay thế. Đó là những gì tôi nghĩ.
Alston

1
@Stallman đây là Java, không phải JavaScript. Ngôn ngữ hoàn toàn khác nhau.
Charles Wood

19

Mã thông số truy vấn

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

HOẶC nếu bạn muốn thoát ký tự trong 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
Sử dụng org.apache.commons.httpclient.util.URIUtildường như là cách hiệu quả nhất để giải quyết vấn đề!
Stéphane Ammar

11

Hello+Worldlà cách trình duyệt sẽ mã hóa dữ liệu biểu mẫu ( application/x-www-form-urlencoded) cho GETyêu cầu và đây là hình thức được chấp nhận chung cho phần truy vấn của URI.

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

Nếu bạn đã gửi yêu cầu này đến một servlet Java, thì servlet sẽ giải mã chính xác giá trị tham số. Thông thường thời gian duy nhất có vấn đề ở đây là nếu mã hóa không khớp.

Nói một cách chính xác, không có yêu cầu nào trong thông số kỹ thuật HTTP hoặc URI rằng phần truy vấn được mã hóa bằng application/x-www-form-urlencodedcác cặp khóa-giá trị; phần truy vấn chỉ cần ở dạng máy chủ web chấp nhận. Trong thực tế, điều này dường như không phải là một vấn đề.

Nói chung sẽ không chính xác khi sử dụng mã hóa này cho các phần khác của URI (ví dụ: đường dẫn). Trong trường hợp đó, bạn nên sử dụng sơ đồ mã hóa như được mô tả trong RFC 3986 .

http://host/Hello%20World

Thêm ở đây .


5

Các câu trả lời khác có thể thay thế chuỗi thủ công, URLEncoder thực sự mã hóa cho định dạng HTML, URIUtil bị bỏ rơi của Apache hoặc sử dụng UrlEscapers của Guava . Cái cuối cùng là tốt, ngoại trừ nó không cung cấp bộ giải mã.

Apache Commons Lang cung cấp URLCodec , mã hóa giải mã theo định dạng URL rfc3986 .

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

Nếu bạn đã sử dụng Spring, bạn cũng có thể chọn sử dụng lớp UriUtils của nó .


6
URLCodec không phải là một giải pháp tốt ở đây vì nó mã hóa các khoảng trắng dưới dạng dấu cộng, nhưng câu hỏi là yêu cầu các khoảng trắng được mã hóa thành% 20.
davidwebster48

3

"+" là chính xác. Nếu bạn thực sự cần% 20, thì hãy tự thay thế Plusses sau đó.


5
Có thể có một vấn đề nếu chuỗi ban đầu thực sự chứa ký tự +.
Alexis Dufrenoy

17
@Traroth - Không hẳn. Một +ký tự trong văn bản gốc được cho là được mã hóa thành %2B.
Ted Hopp

nói rằng đó +là chính xác mà không biết bối cảnh, ít nhất là, phạm vi. Bị hạ bệ. Đọc các câu trả lời khác để biết khi nào + hoặc% 20 sẽ được sử dụng.
Clint Eastwood

@ClintEastwood: Bạn có thể cho tôi biết về bất kỳ usecase nào trong đó ký tự + cho dấu cách không chính xác trong URL không? Ngoại trừ khi có một trình phân tích cú pháp URL không tuân thủ ở phía bên kia?
Daniel

@Daniel chắc chắn, không nói "không chính xác" nhưng không phù hợp? Đúng. Các công cụ phân tích thường sử dụng các tham số truy vấn với các giá trị được phân tách bằng một ký tự nhất định, ví dụ: "+". Trong trường hợp đó, sử dụng "+" thay vì "% 20" sẽ là sai. "+" được sử dụng để thoát các khoảng trắng trong một biểu mẫu, trong khi "mã hóa phần trăm" (còn gọi là mã hóa URL) được định hướng nhiều hơn cho các URL.
Clint Eastwood


2

Điều này làm việc cho tôi

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

1

Mặc dù khá cũ, tuy nhiên một phản ứng nhanh chóng:

Spring cung cấp UriUtils - với điều này, bạn có thể chỉ định cách mã hóa và phần nào liên quan đến URI, ví dụ:

encodePathSegment
encodePort
encodeFragment
encodeUriVariables
....

Tôi sử dụng chúng vì chúng tôi đã sử dụng Spring, tức là không cần thư viện bổ sung!



0

Tôi đang sử dụng phương pháp sai? Phương pháp chính xác tôi nên sử dụng là gì?

Có, phương thức này java.net.URLEncoder.encode không được thực hiện để chuyển đổi "" thành "20%" theo thông số kỹ thuật ( nguồn ).

Ký tự khoảng trắng "" được chuyển đổi thành dấu cộng "+".

Ngay cả đây không phải là phương pháp chính xác, bạn có thể sửa đổi điều này thành: System.out.println(java.net.URLEncoder.encode("Hello World", "UTF-8").replaceAll("\\+", "%20"));chúc một ngày tốt lành =).


Bạn đang đề xuất sử dụng một phương thức không đầy đủ ( URLEncoder.encode) và vá nó bằng cách sử dụng phương pháp replaceAllnày chỉ hoạt động trong trường hợp cụ thể này. Sử dụng đúng lớp và phương thức thay thế, xem các câu trả lời khác.
pyb

@pyb có vẻ như bạn không thể hiểu những gì tôi đã viết. Tôi chưa bao giờ nói "Tôi đề nghị sử dụng nó", tôi nói "bạn có thể". Xin vui lòng đọc và hiểu trước khi bạn viết.
Pregunton

Đây là một trang web câu hỏi và câu trả lời, không phải là một bảng tin thông thường nơi mọi người trò chuyện. Nếu bạn có ý kiến ​​phụ, sử dụng các ý kiến. Nói dài hơn? Sử dụng trò chuyện. Đừng đăng mã mà bạn không đồng ý làm câu trả lời. Xin vui lòng đọc và hiểu các quy tắc của trang web này trước khi đóng góp và giảng bài cho người khác.
pyb

1
Tôi ủng hộ nó trở lại bởi vì hầu hết các giải pháp khác cung cấp cùng một lời khuyên. Không có "trường hợp cụ thể" nào được cung cấp để chứng minh phương pháp này sai. Sử dụng apache commons với các khối bắt hoặc thử phụ thuộc là quá nhiều rắc rối cho một phương thức có thể được vá một cách hiệu quả với thay thế.
Eugene Kartoyev

-2

SỬ DỤNG MyUrlEncode.URLencoding (Chuỗi url, Chuỗi enc) để xử lý sự cố

    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
phát minh lại bánh xe, thêm mã siêu dễ bị lỗi vào cơ sở mã gần như luôn luôn là một quyết định tồi.
Clint Eastwood

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.