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
Формы, представленные с этим типом содержимого, должны быть закодированы следующим образом:
- Имена и значения элементов управления экранированы. Символы пробела заменяются на `+'
Вам придется заменить его, например:
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"));
хорошего дня =).