URLEncoder non è in grado di tradurre il carattere dello spazio


179

mi aspetto

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

per produrre:

Hello%20World

(20 è il codice esadecimale ASCII per lo spazio)

Tuttavia, quello che ottengo è:

Hello+World

Sto usando il metodo sbagliato? Qual è il metodo corretto che dovrei usare?


3
il nome della classe è davvero confuso e molte persone lo hanno usato in modo errato. tuttavia non se ne accorgono, perché quando viene applicato URLDecoder, il valore originale viene ripristinato, quindi + o% 20 non importa davvero per loro.
indiscutibile il

Risposte:


227

Questo si comporta come previsto. I URLEncoderimplementa le specifiche HTML per come codificare gli URL nei moduli HTML.

Dai javadocs :

Questa classe contiene metodi statici per convertire una stringa nel formato MIME application / x-www-form-urlencoded.

e dalle specifiche HTML :

application / x-www-form-urlencoded

I moduli inviati con questo tipo di contenuto devono essere codificati come segue:

  1. I nomi e i valori di controllo sono sottoposti a escape. I caratteri dello spazio sono sostituiti da `+ '

Dovrai sostituirlo, ad esempio:

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

19
bene Questa è davvero una risposta, piuttosto che sostituire non c'è una libreria Java o una funzione per eseguire l'attività /?
co2f2e,

5
Il segno più deve essere evitatot.println(java.net.URLEncoder.encode("Hello World", "UTF-8").replace("\\+", "%20"));
George

26
@congliu non è corretto - probabilmente stai pensando di sostituire All () che funziona con regex - sostituire () è una semplice sostituzione della sequenza di caratteri.
CupawnTae

12
Sì, @congliu è un buon modo: URLEncoder.encode ("Myurl", "utf-8"). ReplaceAll ("\\ +", "% 20");
eento,

9
@ClintEastwood Questa risposta incoraggia l'uso di java.net.URLEncoder che non svolge il compito di quello che era stato inizialmente richiesto. E quindi questa risposta suggerisce una patch, usando sostituisci (), sopra di essa. Perchè no? Perché questa soluzione è soggetta a bug e potrebbe portare ad altre 20 domande simili ma con un carattere diverso. Ecco perché ho detto che era miope.
pyb,

57

Uno spazio è codificato %20negli URL e +nei dati inviati nei moduli (tipo di contenuto application / x-www-form-urlencoded). Hai bisogno del primo.

Utilizzando Guava :

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

Puoi usare UrlEscapers :

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

Non usare String.replace, questo codificherebbe solo lo spazio. Utilizzare invece una libreria.


Funziona anche per Android, com.google.guava: guava: 22.0-rc1-android.
Bevor

1
@Bevor rc1 significa Candidato alla prima versione, ovvero una versione non ancora approvata per la versione generale. Se puoi, scegli una versione senza snapshot, alpha, beta, rc in quanto sono noti per contenere bug.
pyb,

1
@pyb Grazie, ma aggiornerò comunque le librerie quando il mio progetto sarà finito. Significa che non andrò a prode senza le versioni finali. E ci vogliono ancora molte settimane, quindi immagino che ci sia una versione finale.
Bevor

1
Sfortunatamente, Guava non fornisce un decodificatore, a differenza di URLCodec di Apache .
Benny Bottema

26

Questa classe esegue la application/x-www-form-urlencodedcodifica di tipo anziché la codifica percentuale, pertanto la sostituzione con +è un comportamento corretto.

Da javadoc:

Quando si codifica una stringa, si applicano le seguenti regole:

  • I caratteri alfanumerici da "a" a "z", da "A" a "Z" e da "0" a "9" rimangono gli stessi.
  • I caratteri speciali ".", "-", "*" e "_" rimangono gli stessi.
  • Il carattere spazio "" viene convertito in un segno più "+".
  • Tutti gli altri caratteri non sono sicuri e vengono prima convertiti in uno o più byte utilizzando uno schema di codifica. Quindi ogni byte è rappresentato dalla stringa di 3 caratteri "% xy", dove xy è la rappresentazione esadecimale a due cifre del byte. Lo schema di codifica consigliato da utilizzare è UTF-8. Tuttavia, per motivi di compatibilità, se non viene specificata una codifica, viene utilizzata la codifica predefinita della piattaforma.

@axtavt Bella spiegazione. Ma ho ancora alcune domande. Nel url, lo spazio dovrebbe essere interpretato come %20. Quindi dobbiamo fare url.replaceAll("\\+", "%20")? E se è javascript, non dovremmo usare la escapefunzione. Usa encodeURIo encodeURIComponentinvece. È quello che pensavo.
Alston,

1
@Stallman questo è Java, non JavaScript. Lingue totalmente diverse.
Charles Wood,

19

Codifica parametri query

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

O se si desidera sfuggire ai caratteri all'interno dell'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
L'utilizzo org.apache.commons.httpclient.util.URIUtilsembra essere il modo più efficiente per risolvere il problema!
Stéphane Ammar,

11

Hello+Worldè il modo in cui un browser codificherà i dati del modulo ( application/x-www-form-urlencoded) per una GETrichiesta e questo è il modulo generalmente accettato per la parte della query di un URI.

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

Se si invia questa richiesta a un servlet Java, il servlet decodifica correttamente il valore del parametro. Di solito l'unica volta che ci sono problemi qui è se la codifica non corrisponde.

A rigor di termini, nelle specifiche HTTP o URI non è richiesto che la parte della query venga codificata utilizzando application/x-www-form-urlencodedcoppie chiave-valore; la parte della query deve solo essere nella forma accettata dal web server. In pratica, è improbabile che ciò costituisca un problema.

In genere sarebbe errato utilizzare questa codifica per altre parti dell'URI (il percorso ad esempio). In tal caso, è necessario utilizzare lo schema di codifica come descritto in RFC 3986 .

http://host/Hello%20World

Più qui .


5

Le altre risposte presentano una sostituzione manuale della stringa, URLEncoder che in realtà codifica per il formato HTML, l' URIUtil abbandonato di Apache , o usando UrlEscapers di Guava . L'ultimo va bene, tranne per il fatto che non fornisce un decodificatore.

Apache Commons Lang fornisce URLCodec , che codifica e decodifica in base al formato URL rfc3986 .

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

Se stai già utilizzando Spring, puoi anche scegliere di usare anche la sua classe UriUtils .


6
URLCodec non è una buona soluzione qui perché codifica gli spazi come vantaggi, ma la domanda è chiedere che gli spazi vengano codificati come% 20.
davidwebster48,


3

"+" è corretto. Se hai davvero bisogno di% 20, quindi sostituisci tu stesso i Plusses.


5
Potrebbe esserci un problema se la stringa iniziale conteneva davvero un carattere +.
Alexis Dufrenoy,

17
@Traroth - Non proprio. Un +carattere nel testo originale dovrebbe essere codificato come %2B.
Ted Hopp,

dire che +è corretto senza conoscere il contesto è, almeno, pedante. Downvoted. Leggi altre risposte per sapere quando utilizzare + o% 20.
Clint Eastwood,

@ClintEastwood: puoi parlarmi di un caso d'uso in cui il carattere + per gli spazi non è corretto negli URL? Tranne quando c'è un parser URL non conforme sull'altro lato?
Daniel,

@Daniel certo, non dicendo "errato" ma inadatto? sì. Gli strumenti di analisi utilizzano spesso parametri di query con valori separati da un determinato carattere, ad esempio "+". In tal caso, l'utilizzo di "+" invece di "% 20" sarebbe errato. "+" viene utilizzato per sfuggire agli spazi in un modulo, mentre la "codifica percentuale" (ovvero la codifica URL) è più orientata agli URL.
Clint Eastwood,

2

Questo ha funzionato per me

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

1

Sebbene piuttosto vecchio, tuttavia una risposta rapida:

Spring fornisce UriUtils: con questo puoi specificare come codificare e quale parte è correlata da un URI, ad es.

encodePathSegment
encodePort
encodeFragment
encodeUriVariables
....

Li uso perché stiamo già utilizzando Spring, cioè non è necessaria alcuna libreria aggiuntiva!



0

Sto usando il metodo sbagliato? Qual è il metodo corretto che dovrei usare?

Sì, questo metodo java.net.URLEncoder.encode non è stato creato per convertire "" in "20%" in base alle specifiche ( sorgente ).

Il carattere spazio "" viene convertito in un segno più "+".

Anche questo non è il metodo corretto, puoi modificarlo in: System.out.println(java.net.URLEncoder.encode("Hello World", "UTF-8").replaceAll("\\+", "%20"));have a nice day =).


Stai suggerendo di usare un metodo che non è adeguato ( URLEncoder.encode) e di correggerlo usando replaceAllche funzionerebbe solo in questo caso specifico. Utilizzare invece la classe e il metodo corretti, vedere altre risposte.
pyb,

@pyb sembra che tu non riesca a capire cosa ho scritto. Non ho mai detto "Suggerisco di usarlo", ho detto "puoi". Si prega di leggere e comprendere prima di scrivere.
Pregunton,

Questo è un sito di domande e risposte, non una normale bacheca in cui le persone chattano. Se hai commenti collaterali, usa i commenti. Parlare più? Usa la chat. Non pubblicare codice con cui non sei d'accordo come risposta. Si prega di leggere e comprendere le regole di questo sito prima di contribuire e dare lezioni agli altri.
pyb,

1
Lo sto rivalutando perché la maggior parte delle altre soluzioni fornisce lo stesso consiglio. Non sono stati forniti "casi specifici" per dimostrare che questo metodo è errato. L'uso di commons apache con blocchi o dipendenze try-catch è troppo seccante per un metodo che può essere efficacemente patchato con replaceAll.
Eugene Kartoyev,

-2

UTILIZZARE MyUrlEncode.URLencoding (String url, String enc) per gestire il problema

    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
reinventare la ruota, aggiungere un codice soggetto a super errori in una base di codice è quasi sempre una decisione sbagliata.
Clint Eastwood,

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.