URLEncoder не в змозі перекласти символ простору


179

Я чекаю

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

вивести:

Hello%20World

(20 - це шістнадцятковий код ASCII для простору)

Однак я отримую:

Hello+World

Чи використовую я неправильний метод? Який правильний метод я повинен використовувати?


3
назва класу справді заплутана, і багато людей неправильно його вживали. однак вони цього не помічають, оскільки при застосуванні URLDecoder початкове значення відновлюється, тому + або% 20 для них насправді не має значення.
незаперечний

Відповіді:


227

Це поводиться так, як очікувалося. TheURLEncoder Реалізує HTML специфікації про те , як кодувати URL - адреси в HTML - формах.

З javadocs :

Цей клас містить статичні методи перетворення String у формат MIME-формату application / x-www-form-urlencoded.

та із специфікації 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 це невірно - ви, напевно, думаєте про substituAll (), який працює з регулярним виразом - заміна () - це проста заміна послідовностей символів.
CupawnTae

12
Так @congliu, хороший спосіб: URLEncoder.encode ("Myurl", "utf-8"). SubstituAll ("\\ +", "% 20");
eento

9
@ClintEastwood Ця відповідь заохочує використання java.net.URLEncoder, який не відповідає тому, що було запропоновано спочатку. І тому ця відповідь пропонує накладати виправлення, використовуючи заміну (). Чому ні? Тому що це рішення схильне до помилок і може спричинити ще 20 подібних питань, але з різним характером. Тому я сказав, що це короткозоро.
піб

57

Простір кодується як %20в URL-адресах, так і +у формах, що подаються даними (тип контентної програми / x-www-form-urlencoded). Вам потрібна перша.

Використання Guava :

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.
Бевор

1
@Bevor rc1 означає Candidate 1st Release, тобто версію, яка ще не затверджена для загального випуску. Якщо ви можете, виберіть версію без знімка, альфа, бета, rc, оскільки, як відомо, вони містять помилки.
піб

1
@pyb Дякую, але я все одно оновлю вкладки, коли мій проект буде закінчений. Значить, я не піду продавати без остаточних версій. І це ще триває багато тижнів, тож я думаю, тоді є остаточна версія.
Бевор

1
На жаль, Guava не надає декодер, на відміну від URLCodec Apache .
Бенні Боттема

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, ми не повинні використовувати escapeфункцію. Використовуйте encodeURIабо encodeURIComponentзамість цього. Це те, що я думав.
Елстон

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здається, є найбільш ефективним способом вирішення проблеми!
Стефан Аммар

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

Інші відповіді або представляють ручну заміну рядка, URLEncoder, який фактично кодує формат HTML, покинутий URIUtil Apache , або використовують UrlEscapers Guava . Останній добре, за винятком того, що він не забезпечує декодер.

Apache Commons Lang надає URLCodec , який кодує і декодує відповідно до формату URL rfc3986 .

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

Якщо ви вже використовуєте Spring, ви можете також вибрати його клас UriUtils .


6
Тут URLCodec не є хорошим рішенням, оскільки він кодує пробіли як плюси, але питання полягає в тому, щоб пробіли були кодовані як% 20.
davidwebster48

3

"+" правильно. Якщо вам дійсно потрібен% 20, то замініть себе потім.


5
Може виникнути проблема, якщо початковий рядок дійсно містив символ +.
Олексій Дуфреной

17
@Traroth - Не дуже. +Символ в початковому тексті повинен бути закодований як %2B.
Тед Хопп

сказати, що +правильно, не знаючи контексту, є, принаймні, педантичним. Захищений. Прочитайте інші відповіді, щоб знати, коли потрібно використовувати + або% 20.
Клінт Іствуд

@ClintEastwood: Чи можете ви розповісти про будь-яку корисну скриньку в тому, що символ + для пробілів у URL-адресах невірний? За винятком випадків, коли з іншого боку є невідповідний аналізатор URL-адрес?
Даніель

@Daniel впевнений, не каже "неправильно", але непридатний? так. Інструменти Analytics часто використовують параметри запитів зі значеннями, розділеними певним символом, наприклад "+". У цьому випадку використання "+" замість "% 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 не був зроблений для перетворення "" в "20%" відповідно до специфікації ( джерело ).

Пробільний символ "" перетворюється в знак "+".

Навіть це не правильний метод, ви можете змінити це на: System.out.println(java.net.URLEncoder.encode("Hello World", "UTF-8").replaceAll("\\+", "%20"));приємний день =).


Ви пропонуєте скористатися методом, який не є адекватним ( URLEncoder.encode), і виправити його, використовуючи replaceAllякий працював би лише у цьому конкретному випадку. Використовуйте натомість правильний клас та метод, дивіться інші відповіді.
піб

@pyb виглядає так, що ви не можете зрозуміти, що я написав. Я ніколи не казав "я пропоную використовувати", я сказав "можна". Будь ласка, прочитайте та зрозумійте, перш ніж писати.
Прегунтон

Це веб-сайт із запитаннями та відповідями, а не звичайна дошка оголошень, де люди спілкуються в чаті. Якщо у вас є побічні коментарі, використовуйте коментарі. Триваліші розмови? Використовуйте чат. Не публікуйте код, з яким ви не згодні, як відповідь. Будь ласка, прочитайте та зрозумійте правила цього веб-сайту, перш ніж докладати та читати лекції іншим.
піб

1
Я повертаю цю заяву назад, оскільки більшість інших рішень дають ті самі поради. Жодних «конкретних випадків» не було передбачено, щоб довести цей метод неправильним. Використання спільноти apache з блоками пробного вловлювання або залежностями - це занадто клопоту для методу, який може бути ефективно виправлений за допомогою substituAll.
Євген Картоєв

-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
переосмислити колесо, додавання супер-схильного до помилок коду до бази коду майже завжди є поганим рішенням.
Клінт Іствуд

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.