URLEncoder не умеет переводить символы пробела

Я ожидаю

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

вывести:

Hello%20World

(20 - шестнадцатеричный код ASCII для пробела)

Однако то, что я получаю, это:

Hello+World

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

19 ответов

Решение

Это ведет себя как ожидалось. URLEncoder реализует спецификации HTML для того, как кодировать URL-адреса в формах HTML.

Из Javadocs:

Этот класс содержит статические методы для преобразования строки в формат MIME application/x-www-form-urlencoded.

и из спецификации HTML:

применение / х-WWW-форм-urlencoded

Формы, представленные с этим типом содержимого, должны быть закодированы следующим образом:

  1. Имена и значения элементов управления экранированы. Символы пробела заменяются на `+'

Вам придется заменить его, например:

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

Пробел закодирован в %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, это только кодирует пространство. Используйте библиотеку вместо.

Этот класс выступают application/x-www-form-urlencoded-типа кодирования, а не процентное кодирование, поэтому замена с + это правильное поведение.

Из Javadoc:

При кодировании строки применяются следующие правила:

  • Буквенно-цифровые символы от "a" до "z", от "A" до "Z" и от "0" до "9" остаются неизменными.
  • Специальные символы ".", "-", "*" и "_" остаются прежними.
  • Символ пробела "" преобразуется в знак плюс "+".
  • Все остальные символы небезопасны и сначала преобразуются в один или несколько байтов с использованием некоторой схемы кодирования. Затем каждый байт представляется трехсимвольной строкой "%xy", где xy - это шестнадцатеричное представление байта из двух цифр. Рекомендуемая схема кодирования - UTF-8. Однако из соображений совместимости, если кодировка не указана, используется кодировка платформы по умолчанию.

Кодировать параметры запроса

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

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

Больше здесь.

Если вы хотите кодировать компоненты пути 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 "";
}

Класс URI также можно использовать для кодирования различных частей или целых URI.

Другие ответы представляют либо ручную замену строки, 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.

Хотя довольно старый, тем не менее быстрый ответ:

Spring предоставляет UriUtils - с этим вы можете указать, как кодировать и с какой частью это связано с URI, например

encodePathSegment
encodePort
encodeFragment
encodeUriVariables
....

Я использую их, потому что мы уже используем Spring, то есть никакой дополнительной библиотеки не требуется!

Только что боролся с этим и на Android, сумел наткнуться на Uri.encode(String, String), в то время как специфический для android (android.net.Uri) может быть полезен для некоторых.

статическое строковое кодирование (String s, String allow)

https://developer.android.com/reference/android/net/Uri.html

Это не однострочник, но вы можете использовать:

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

Это даст вам результат:

      https://some-host.net/dav/files/selling_Rosetta%20Stone%20Case%20Study.png.aes

"+" - это правильно. Если вам действительно нужен%20, затем замените плюсы самостоятельно.

Если вы используете причал, тогда org.eclipse.jetty.util.URIUtil решит вопрос.

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

Это сработало для меня

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

Я уже использовал Feign, поэтому UriUtils был мне доступен, а Spring UrlUtils — нет.

      <!-- https://mvnrepository.com/artifact/io.github.openfeign/feign-core -->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-core</artifactId>
    <version>11.8</version>
</dependency>

Мой тестовый код Feign:

      import feign.template.UriUtils;

System.out.println(UriUtils.encode("Hello World"));

Выходы:

Привет%20Мир

Как предполагает класс, он кодирует URI, а не URL-адреса, но OP спрашивал об URI, а не об URL-адресах.

      System.out.println(UriUtils.encode("https://some-host.net/dav/files/selling_Rosetta Stone Case Study.png.aes"));

Выходы:

https%3A%2F%2Fsome-host.net%2Fdav%2Ffiles%2Fselling_Rosetta%20Stone%20Case%20Study.png.aes

Попробуйте следующий подход:

Добавить новую зависимость

      <!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-catalina -->
<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-catalina</artifactId>
    <version>10.0.13</version>
</dependency>

Теперь сделайте следующее:

      String str = "Hello+World"; // For "Hello World", decoder is not required
// import java.net.URLDecoder;
String newURL = URLDecoder.decode(str, StandardCharsets.UTF_8);
// import org.apache.catalina.util.URLEncoder;
System.out.println(URLEncoder.DEFAULT.encode(newURL, StandardCharsets.UTF_8));

Вы получите вывод как:

      Hello%20World

ИСПОЛЬЗУЙТЕ 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();
    }

}

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

Да, этот метод java.net.URLEncoder.encode не был создан для преобразования "" в "20%" в соответствии со спецификацией ( источник).

Символ пробела "" преобразуется в знак плюс "+".

Даже если это не правильный метод, вы можете изменить это следующим образом: System.out.println(java.net.URLEncoder.encode("Hello World", "UTF-8").replaceAll("\\+", "%20"));хорошего дня =).

Проверьте класс java.net.URI.

Использовать набор символов "ISO-8859-1"для URLEncoder

Другие вопросы по тегам