URLEncoder kann Leerzeichen nicht übersetzen

177

Ich erwarte

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

zur Ausgabe:

Hello%20World

(20 ist ASCII-Hex-Code für Leerzeichen)

Was ich jedoch bekomme, ist:

Hello+World

Benutze ich die falsche Methode? Was ist die richtige Methode, die ich verwenden sollte?

Cheok Yan Cheng
quelle
3
Der Klassenname ist in der Tat verwirrend und viele Leute haben ihn falsch verwendet. Sie bemerken es jedoch nicht, da beim Anwenden von URLDecoder der ursprüngliche Wert wiederhergestellt wird, sodass + oder% 20 für sie nicht wirklich wichtig sind.
Unbestreitbarer

Antworten:

226

Dies verhält sich wie erwartet. Das URLEncoderimplementiert die HTML-Spezifikationen zum Codieren von URLs in HTML-Formularen.

Aus den Javadocs :

Diese Klasse enthält statische Methoden zum Konvertieren eines Strings in das MIME-Format application / x-www-form-urlencoded.

und aus der HTML-Spezifikation :

application / x-www-form-urlencoded

Mit diesem Inhaltstyp eingereichte Formulare müssen wie folgt codiert werden:

  1. Kontrollnamen und -werte werden maskiert. Leerzeichen werden durch "+" ersetzt.

Sie müssen es ersetzen, zB:

System.out.println(java.net.URLEncoder.encode("Hello World", "UTF-8").replace("+", "%20"));
Dogbane
quelle
19
Nun, dies ist in der Tat eine Antwort, anstatt zu ersetzen, gibt es nicht eine Java-Bibliothek oder eine Funktion, um die Aufgabe auszuführen /?
co2f2e
5
Das Pluszeichen muss entkomment.println(java.net.URLEncoder.encode("Hello World", "UTF-8").replace("\\+", "%20"));
George
25
@congliu das ist falsch - Sie denken wahrscheinlich an replaceAll (), das mit Regex funktioniert - replace () ist ein einfaches Ersetzen der Zeichenfolge.
CupawnTae
12
Ja @congliu der gute Weg ist: URLEncoder.encode ("Myurl", "utf-8"). ReplaceAll ("\\ +", "% 20");
Eento
9
@ClintEastwood Diese Antwort empfiehlt die Verwendung von java.net.URLEncoder, der nicht die Aufgabe erfüllt, die ursprünglich gestellt wurde. Daher schlägt diese Antwort einen Patch mit replace () vor. Warum nicht? Weil diese Lösung fehleranfällig ist und zu 20 ähnlichen Fragen führen kann, die jedoch einen anderen Charakter haben. Deshalb habe ich gesagt, das sei kurzsichtig.
Pyb
57

Ein Leerzeichen wird %20in URLs und in +in Formularen übermittelten Daten codiert (Inhaltstyp application / x-www-form-urlencoded). Du brauchst das erstere.

Verwenden von Guave :

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

Sie können UrlEscapers verwenden :

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

Verwenden Sie String.replace nicht, dies würde nur den Speicherplatz codieren. Verwenden Sie stattdessen eine Bibliothek.

pyb
quelle
Es funktioniert auch für Android, com.google.guava: guava: 22.0-rc1-android.
Bevor
1
@Bevor rc1 bedeutet 1st Release Candidate, dh eine Version, die noch nicht für die allgemeine Veröffentlichung zugelassen ist. Wenn Sie können, wählen Sie eine Version ohne Snapshot, Alpha, Beta, RC, da diese bekanntermaßen Fehler enthalten.
Pyb
1
@pyb Danke, aber ich werde die Bibliotheken trotzdem aktualisieren, wenn mein Projekt abgeschlossen ist. Das heißt, ich werde nicht ohne endgültige Versionen zum Prod gehen. Und es dauert immer noch viele Wochen, also gibt es dann wohl eine endgültige Version.
Bevor
1
Leider bietet Guava im Gegensatz zu Apaches URLCodec keinen Decoder an .
Benny Bottema
26

Diese Klasse führt application/x-www-form-urlencodedeher eine Codierung vom Typ als eine prozentuale Codierung durch, daher ist das Ersetzen durch +ein korrektes Verhalten.

Aus Javadoc:

Beim Codieren eines Strings gelten die folgenden Regeln:

  • Die alphanumerischen Zeichen "a" bis "z", "A" bis "Z" und "0" bis "9" bleiben gleich.
  • Die Sonderzeichen ".", "-", "*" und "_" bleiben gleich.
  • Das Leerzeichen "" wird in ein Pluszeichen "+" umgewandelt.
  • Alle anderen Zeichen sind unsicher und werden zuerst mithilfe eines Codierungsschemas in ein oder mehrere Bytes konvertiert. Dann wird jedes Byte durch die 3-stellige Zeichenfolge "% xy" dargestellt, wobei xy die zweistellige hexadezimale Darstellung des Bytes ist. Das empfohlene Codierungsschema ist UTF-8. Wenn jedoch aus Kompatibilitätsgründen keine Codierung angegeben ist, wird die Standardcodierung der Plattform verwendet.
axtavt
quelle
@axtavt Schöne Erklärung. Aber ich habe noch einige Fragen. In der urlsollte der Raum als interpretiert werden %20. Also müssen wir tun url.replaceAll("\\+", "%20")? Und wenn es Javascript ist, sollten wir keine escapeFunktion verwenden. Verwenden Sie encodeURIoder encodeURIComponentstattdessen. Das ist was ich dachte.
Alston
1
@ Stallman das ist Java, nicht JavaScript. Ganz andere Sprachen.
Charles Wood
19

Abfrageparameter codieren

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

ODER wenn Sie Zeichen innerhalb der URI umgehen möchten

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
quelle
3
Die Verwendung org.apache.commons.httpclient.util.URIUtilscheint der effizienteste Weg zu sein, um das Problem zu lösen!
Stéphane Ammar
11

Hello+WorldAuf diese Weise codiert ein Browser Formulardaten ( application/x-www-form-urlencoded) für eine GETAnforderung. Dies ist das allgemein akzeptierte Formular für den Abfrageteil einer URI.

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

Wenn Sie diese Anforderung an ein Java-Servlet senden, dekodiert das Servlet den Parameterwert korrekt. Normalerweise treten hier nur dann Probleme auf, wenn die Codierung nicht übereinstimmt.

Genau genommen ist in den HTTP- oder URI-Spezifikationen nicht erforderlich, dass der Abfrageteil mithilfe von application/x-www-form-urlencodedSchlüssel-Wert-Paaren codiert wird. Der Abfrageteil muss nur die Form haben, die der Webserver akzeptiert. In der Praxis ist dies wahrscheinlich kein Problem.

Es wäre im Allgemeinen falsch, diese Codierung für andere Teile des URI (z. B. den Pfad) zu verwenden. In diesem Fall sollten Sie das in RFC 3986 beschriebene Codierungsschema verwenden .

http://host/Hello%20World

Mehr hier .

McDowell
quelle
5

Die anderen Antworten enthalten entweder einen manuellen String-Ersatz, URLEncoder, der tatsächlich für das HTML-Format codiert, Apaches aufgegebenes URIUtil oder die Verwendung von Guavas UrlEscapers . Der letzte ist in Ordnung, außer dass er keinen Decoder bietet.

Apache Commons Lang bietet den URLCodec , der gemäß dem URL-Format rfc3986 codiert und decodiert .

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

Wenn Sie Spring bereits verwenden, können Sie auch die UriUtils- Klasse verwenden.

Benny Bottema
quelle
6
URLCodec ist hier keine gute Lösung, da es Leerzeichen als Pluspunkte codiert, aber die Frage lautet, ob Leerzeichen als% 20 codiert werden sollen.
Davidwebster48
3

"+" ist richtig. Wenn Sie wirklich% 20 benötigen, ersetzen Sie die Plusses anschließend selbst.

Daniel
quelle
5
Möglicherweise liegt ein Problem vor, wenn die ursprüngliche Zeichenfolge tatsächlich ein + -Zeichen enthält.
Alexis Dufrenoy
17
@ Traroth - Nicht wirklich. Ein +Zeichen im Originaltext soll als codiert sein %2B.
Ted Hopp
zu sagen, dass +das richtig ist, ohne den Kontext zu kennen, ist zumindest pedantisch. Abgestimmt. Lesen Sie andere Antworten, um zu erfahren, wann + oder% 20 verwendet werden sollen.
Clint Eastwood
@ClintEastwood: Können Sie mir einen Anwendungsfall mitteilen, bei dem das + -Zeichen für Leerzeichen in URLs nicht korrekt ist? Außer wenn es auf der anderen Seite einen nicht konformen URL-Parser gibt?
Daniel
@ Daniel sicher, nicht "falsch" sagen, aber ungeeignet? Ja. Analytics-Tools verwenden häufig Abfrageparameter mit Werten, die durch ein bestimmtes Zeichen getrennt sind, z. B. "+". In diesem Fall wäre die Verwendung von "+" anstelle von "% 20" falsch. "+" wird verwendet, um Leerzeichen in einem Formular zu maskieren, während die "prozentuale Codierung" (auch als URL-Codierung bezeichnet) eher auf URLs ausgerichtet ist.
Clint Eastwood
2

Das hat bei mir funktioniert

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

Obwohl ziemlich alt, dennoch eine schnelle Antwort:

Spring bietet UriUtils - hier können Sie angeben, wie codiert werden soll und welcher Teil von einem URI stammt, z

encodePathSegment
encodePort
encodeFragment
encodeUriVariables
....

Ich benutze sie, weil wir bereits Spring verwenden, dh es ist keine zusätzliche Bibliothek erforderlich!

Löwe
quelle
0

Schauen Sie sich die Klasse java.net.URI an.

Fredrik Widerberg
quelle
0

Benutze ich die falsche Methode? Was ist die richtige Methode, die ich verwenden sollte?

Ja, diese Methode java.net.URLEncoder.encode wurde nicht für die Konvertierung von "" in "20%" gemäß Spezifikation ( Quelle ) erstellt.

Das Leerzeichen "" wird in ein Pluszeichen "+" umgewandelt.

Auch wenn dies nicht die richtige Methode ist, können Sie dies ändern, um: System.out.println(java.net.URLEncoder.encode("Hello World", "UTF-8").replaceAll("\\+", "%20"));einen schönen Tag zu haben =).

Pregunton
quelle
Sie schlagen vor, eine Methode zu verwenden, die nicht ausreichend ist ( URLEncoder.encode), und sie mit einer Methode zu patchen, replaceAlldie nur in diesem speziellen Fall funktioniert. Verwenden Sie stattdessen die richtige Klasse und Methode, siehe andere Antworten.
Pyb
@pyb sieht so aus, als ob du nicht verstehen kannst, was ich geschrieben habe. Ich habe nie gesagt "Ich schlage vor, es zu verwenden", ich sagte "Sie können". Bitte lesen und verstehen Sie, bevor Sie schreiben.
Pregunton
Dies ist eine Frage- und Antwort-Website, kein reguläres Message Board, auf dem Leute chatten. Wenn Sie Nebenkommentare haben, verwenden Sie die Kommentare. Längeres Gespräch? Nutze den Chat. Geben Sie keine Postleitzahl, mit der Sie nicht einverstanden sind, als Antwort an. Bitte lesen und verstehen Sie die Regeln dieser Website, bevor Sie Beiträge leisten und Vorträge halten.
Pyb
1
Ich stimme es zurück, weil die meisten anderen Lösungen den gleichen Rat geben. Es wurden keine "speziellen Fälle" angegeben, um zu beweisen, dass diese Methode falsch ist. Die Verwendung von Apache Commons mit Try-Catch-Blöcken oder Abhängigkeiten ist zu aufwändig für eine Methode, die effektiv mit replaceAll gepatcht werden kann.
Eugene Kartoyev
-2

USE MyUrlEncode.URLencoding (String url, String enc) , das Problem zu umgehen

    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
quelle
9
Das Rad neu zu erfinden und einer Codebasis super fehleranfälligen Code hinzuzufügen, ist fast immer eine schlechte Entscheidung.
Clint Eastwood
-6

Verwenden Sie den Zeichensatz " ISO-8859-1" für URLEncoder

Akhil Sikri
quelle