URLEncoder ne peut pas traduire le caractère espace

179

je m'attends à

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

pour sortir:

Hello%20World

(20 est le code hexadécimal ASCII pour l'espace)

Cependant, ce que j'obtiens est:

Hello+World

Est-ce que j'utilise la mauvaise méthode? Quelle est la bonne méthode que je devrais utiliser?

Cheok Yan Cheng
la source
3
le nom de la classe est en effet déroutant et de nombreuses personnes l'ont mal utilisé. Cependant, ils ne le remarquent pas, car lorsque URLDecoder est appliqué, la valeur d'origine est restaurée, donc + ou% 20 n'a pas vraiment d'importance pour eux.
irréputable le

Réponses:

227

Cela se comporte comme prévu. Le URLEncoderimplémente les spécifications HTML pour savoir comment encoder les URL dans les formulaires HTML.

Depuis les javadocs :

Cette classe contient des méthodes statiques pour convertir une chaîne au format MIME application / x-www-form-urlencoded.

et à partir de la spécification HTML :

application / x-www-form-urlencoded

Les formulaires soumis avec ce type de contenu doivent être codés comme suit:

  1. Les noms et valeurs de contrôle sont échappés. Les caractères d'espacement sont remplacés par «+»

Vous devrez le remplacer, par exemple:

System.out.println(java.net.URLEncoder.encode("Hello World", "UTF-8").replace("+", "%20"));
dogbane
la source
19
bien Ceci est une réponse en effet, plutôt que de remplacer n'y a-t-il pas une bibliothèque java ou une fonction pour effectuer la tâche /?
co2f2e
5
Le signe plus doit être échappét.println(java.net.URLEncoder.encode("Hello World", "UTF-8").replace("\\+", "%20"));
George
26
@congliu c'est incorrect - vous pensez probablement à replaceAll () qui fonctionne avec regex - replace () est un simple remplacement de séquence de caractères.
CupawnTae
12
Oui @congliu le bon moyen est: URLEncoder.encode ("Myurl", "utf-8"). ReplaceAll ("\\ +", "% 20");
eento
9
@ClintEastwood Cette réponse encourage l'utilisation de java.net.URLEncoder qui ne fait pas le travail de ce qui a été initialement demandé. Et donc cette réponse suggère un correctif, en utilisant replace (), en plus. Pourquoi pas? Parce que cette solution est sujette à des bogues et pourrait conduire à 20 autres questions similaires mais avec un caractère différent. C'est pourquoi j'ai dit que c'était à courte vue.
pyb
57

Un espace est encodé %20dans les URL et +dans les formulaires soumis aux données (type de contenu application / x-www-form-urlencoded). Vous avez besoin du premier.

Utilisation de la goyave :

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

Vous pouvez utiliser UrlEscapers :

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

N'utilisez pas String.replace, cela coderait uniquement l'espace. Utilisez plutôt une bibliothèque.

pyb
la source
Cela fonctionne également pour Android, com.google.guava: guava: 22.0-rc1-android.
Bevor
1
@Bevor rc1 signifie 1ère Release Candidate, c'est-à-dire une version non encore approuvée pour la diffusion générale. Si vous le pouvez, choisissez une version sans snapshot, alpha, beta, rc car ils sont connus pour contenir des bogues.
pyb
1
@pyb Merci, mais je mettrai quand même à jour les bibliothèques lorsque mon projet sera terminé. Cela signifie que je n'irai pas produire sans versions finales. Et cela prend encore beaucoup de semaines, donc je suppose qu'il y a une version finale alors.
Bevor
1
Malheureusement, Guava ne fournit pas de décodeur, contrairement à URLCodec d'Apache .
Benny Bottema
26

Cette classe effectue un application/x-www-form-urlencodedcodage de type plutôt qu'un codage en pourcentage, donc le remplacement par +est un comportement correct.

De javadoc:

Lors de l'encodage d'une chaîne, les règles suivantes s'appliquent:

  • Les caractères alphanumériques «a» à «z», «A» à «Z» et «0» à «9» restent les mêmes.
  • Les caractères spéciaux ".", "-", "*" et "_" restent les mêmes.
  • Le caractère espace "" est converti en signe plus "+".
  • Tous les autres caractères ne sont pas sûrs et sont d'abord convertis en un ou plusieurs octets à l'aide d'un schéma de codage. Ensuite, chaque octet est représenté par la chaîne de 3 caractères "% xy", où xy est la représentation hexadécimale à deux chiffres de l'octet. Le schéma de codage recommandé à utiliser est UTF-8. Cependant, pour des raisons de compatibilité, si un encodage n'est pas spécifié, alors l'encodage par défaut de la plateforme est utilisé.
axtavt
la source
@axtavt Belle explication. Mais j'ai encore quelques questions. Dans le url, l'espace doit être interprété comme %20. Alors nous devons faire url.replaceAll("\\+", "%20")? Et si c'est du javascript, nous ne devrions pas utiliser de escapefonction. Utilisez encodeURIou à la encodeURIComponentplace. C'est ce que je pensais.
Alston
1
@Stallman c'est Java, pas JavaScript. Langues totalement différentes.
Charles Wood
19

Encoder les paramètres de requête

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

OU si vous voulez échapper des caractères dans l'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;
 }
fmucar
la source
3
L'utilisation org.apache.commons.httpclient.util.URIUtilsemble être le moyen le plus efficace de résoudre le problème!
Stéphane Ammar
11

Hello+Worldest la façon dont un navigateur encodera les données de formulaire ( application/x-www-form-urlencoded) pour une GETrequête et c'est la forme généralement acceptée pour la partie requête d'un URI.

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

Si vous envoyez cette demande à un servlet Java, le servlet décode correctement la valeur du paramètre. Habituellement, le seul moment où il y a des problèmes ici est si l'encodage ne correspond pas.

À proprement parler, il n'y a aucune exigence dans les spécifications HTTP ou URI que la partie requête soit encodée à l'aide de application/x-www-form-urlencodedpaires clé-valeur; la partie requête doit simplement être sous la forme acceptée par le serveur Web. En pratique, il est peu probable que ce soit un problème.

Il serait généralement incorrect d'utiliser ce codage pour d'autres parties de l'URI (le chemin par exemple). Dans ce cas, vous devez utiliser le schéma de codage décrit dans la RFC 3986 .

http://host/Hello%20World

Plus ici .

McDowell
la source
5

Les autres réponses présentent soit un remplacement manuel de chaîne, URLEncoder qui encode réellement pour le format HTML, URIUtil abandonné d' Apache , soit en utilisant UrlEscapers de Guava . Le dernier est bien, sauf qu'il ne fournit pas de décodeur.

Apache Commons Lang fournit l' URLCodec , qui encode et décode selon le format d'URL rfc3986 .

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

Si vous utilisez déjà Spring, vous pouvez également choisir d'utiliser sa classe UriUtils .

Benny Bottema
la source
6
URLCodec n'est pas une bonne solution ici car il encode les espaces comme des plus, mais la question demande que les espaces soient encodés en% 20.
davidwebster48
3

"+" est correct. Si vous avez vraiment besoin de% 20, remplacez les plus vous-même par la suite.

Daniel
la source
5
Il peut y avoir un problème si la chaîne initiale contenait vraiment un caractère +.
Alexis Dufrenoy
17
@Traroth - Pas vraiment. Un +caractère du texte original est censé être codé comme %2B.
Ted Hopp
dire que +c'est correct sans connaître le contexte est, au moins, pédant. Voté contre. Lisez les autres réponses pour savoir quand + ou% 20 doivent être utilisés.
Clint Eastwood
@ClintEastwood: Pouvez-vous me parler d'un cas d'utilisation dans lequel le caractère + pour les espaces n'est pas correct dans les URL? Sauf quand il y a un analyseur d'URL non conforme de l'autre côté?
Daniel
@Daniel bien sûr, ne disant pas "incorrect" mais inapproprié? Oui. Les outils d'analyse utilisent souvent des paramètres de requête avec des valeurs séparées par un certain caractère, par exemple "+". Dans ce cas, utiliser "+" au lieu de "% 20" serait erroné. "+" est utilisé pour échapper des espaces dans un formulaire, tandis que le "codage en pourcentage" (aka codage d'URL) est plus orienté vers les URL.
Clint Eastwood
2

Cela a fonctionné pour moi

org.apache.catalina.util.URLEncoder ul = new org.apache.catalina.util.URLEncoder().encode("MY URL");
Hitesh Kumar
la source
1

Bien qu'assez vieux, néanmoins une réponse rapide:

Spring fournit UriUtils - avec cela, vous pouvez spécifier comment encoder et quelle partie est-elle liée à partir d'un URI, par exemple

encodePathSegment
encodePort
encodeFragment
encodeUriVariables
....

Je les utilise car nous utilisons déjà Spring, c'est à dire qu'aucune bibliothèque supplémentaire n'est requise!

Leo
la source
0

Consultez la classe java.net.URI.

Fredrik Widerberg
la source
0

Est-ce que j'utilise la mauvaise méthode? Quelle est la bonne méthode que je devrais utiliser?

Oui, cette méthode java.net.URLEncoder.encode n'a pas été conçue pour convertir "" en "20%" selon les spécifications ( source ).

Le caractère espace "" est converti en signe plus "+".

Même ce n'est pas la bonne méthode, vous pouvez modifier ceci pour: passez System.out.println(java.net.URLEncoder.encode("Hello World", "UTF-8").replaceAll("\\+", "%20"));une bonne journée =).

Pregunton
la source
Vous suggérez d'utiliser une méthode qui n'est pas adéquate ( URLEncoder.encode) et de la corriger en utilisant replaceAllce qui ne fonctionnerait que dans ce cas précis. Utilisez plutôt la classe et la méthode appropriées, voir d'autres réponses.
pyb
@pyb semble que vous ne comprenez pas ce que j'ai écrit. Je n'ai jamais dit «je suggère de l'utiliser», j'ai dit «vous pouvez». Veuillez lire et comprendre avant d'écrire.
Pregunton
Il s'agit d'un site Web de questions et réponses, pas d'un babillard électronique régulier où les gens discutent. Si vous avez des commentaires secondaires, utilisez les commentaires. Plus de discussions? Utilisez le chat. Ne postez pas le code avec lequel vous n'êtes pas d'accord comme réponse. Veuillez lire et comprendre les règles de ce site avant de contribuer et de donner des conférences aux autres.
pyb
1
Je vote pour lui parce que la plupart des autres solutions fournissent les mêmes conseils. Aucun "cas particulier" n'a été fourni pour prouver que cette méthode était erronée. Utiliser apache commons avec des blocs try-catch ou des dépendances est trop compliqué pour une méthode qui peut être corrigée efficacement avec replaceAll.
Eugene Kartoyev
-2

UTILISEZ MyUrlEncode.URLencoding (String url, String enc) pour gérer le problème

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

}
IloveIniesta
la source
9
réinventer la roue, ajouter du code très sujet aux erreurs à une base de code est presque toujours une mauvaise décision.
Clint Eastwood
-6

utiliser le jeu de caractères " ISO-8859-1" pour URLEncoder

Akhil Sikri
la source