URLEncoder tidak dapat menerjemahkan karakter ruang

179

Saya mengharapkan

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

untuk output:

Hello%20World

(20 adalah kode ASCII Hex untuk spasi)

Namun, yang saya dapatkan adalah:

Hello+World

Apakah saya menggunakan metode yang salah? Apa metode yang benar yang harus saya gunakan?

Cheok Yan Cheng
sumber
3
nama kelasnya memang membingungkan, dan banyak orang salah menggunakannya. namun mereka tidak menyadarinya, karena ketika URLDecoder diterapkan, nilai asli dipulihkan, jadi + atau% 20 tidak terlalu penting bagi mereka.
Diperbaiki

Jawaban:

227

Ini berperilaku seperti yang diharapkan. The URLEncoderalat Spesifikasi HTML untuk bagaimana encode URL dalam bentuk HTML.

Dari javadocs :

Kelas ini berisi metode statis untuk mengubah String ke format MIME aplikasi / x-www-form-urlencoded.

dan dari Spesifikasi HTML :

application / x-www-form-urlencoded

Formulir yang dikirimkan dengan tipe konten ini harus dikodekan sebagai berikut:

  1. Nama dan nilai kontrol diloloskan. Karakter spasi diganti dengan `+ '

Anda harus menggantinya, misalnya:

System.out.println(java.net.URLEncoder.encode("Hello World", "UTF-8").replace("+", "%20"));
dogbane
sumber
19
baik Ini memang jawaban, daripada mengganti tidak ada perpustakaan java atau fungsi untuk melakukan tugas /?
co2f2e
5
Tanda plus harus dilepaskant.println(java.net.URLEncoder.encode("Hello World", "UTF-8").replace("\\+", "%20"));
George
26
@congliu itu salah - Anda mungkin berpikir tentang replaceAll () yang berfungsi dengan regex - replace () adalah penggantian urutan karakter sederhana.
CupawnTae
12
Ya @congliu cara yang baik adalah: URLEncoder.encode ("Myurl", "utf-8"). ReplaceAll ("\\ +", "% 20");
eento
9
@ClintEastwood Jawaban ini menganjurkan penggunaan java.net.URLEncoder yang tidak melakukan apa yang semula ditanyakan. Dan jawaban ini menyarankan tambalan, menggunakan replace (), di atasnya. Kenapa tidak? Karena solusi ini rawan bug dan dapat menyebabkan 20 pertanyaan serupa lainnya tetapi dengan karakter yang berbeda. Itu sebabnya saya katakan ini picik.
pyb
57

Sebuah ruang dikodekan ke %20dalam URL, dan ke +dalam formulir yang dikirimkan data (aplikasi tipe konten / x-www-form-urlencoded). Anda membutuhkan yang pertama.

Menggunakan jambu biji :

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

Anda dapat menggunakan UrlEscapers :

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

Jangan gunakan String.replace, ini hanya akan menyandikan ruang. Gunakan perpustakaan sebagai gantinya.

pyb
sumber
Ini juga berfungsi untuk Android, com.google.guava: jambu: 22.0-rc1-android.
Bevor
1
@Bevor rc1 berarti Calon Rilis Pertama, yaitu versi yang belum disetujui untuk rilis umum. Jika Anda bisa, pilih versi tanpa snapshot, alfa, beta, rc karena diketahui mengandung bug.
pyb
1
@pyb Terima kasih, tetapi saya tetap akan memperbarui libs ketika proyek saya akan selesai. Berarti, saya tidak akan pergi ke prod tanpa versi final. Dan itu masih membutuhkan banyak minggu, jadi saya kira ada versi finalnya.
Bevor
1
Sayangnya, Guava tidak menyediakan dekoder, tidak seperti URLCodec dari Apache .
Benny Bottema
26

Kelas ini melakukan application/x-www-form-urlencoded-type encoding daripada persen encoding, oleh karena itu mengganti dengan +adalah perilaku yang benar.

Dari javadoc:

Saat menyandikan sebuah String, aturan berikut ini berlaku:

  • Karakter alfanumerik "a" hingga "z", "A" hingga "Z" dan "0" hingga "9" tetap sama.
  • Karakter khusus ".", "-", "*", dan "_" tetap sama.
  • Karakter spasi "" dikonversi menjadi tanda tambah "+".
  • Semua karakter lain tidak aman dan pertama kali dikonversi menjadi satu atau lebih byte menggunakan beberapa skema penyandian Kemudian setiap byte diwakili oleh string 3-karakter "% xy", di mana xy adalah representasi dua digit heksadesimal dari byte. Skema pengkodean yang direkomendasikan untuk digunakan adalah UTF-8. Namun, untuk alasan kompatibilitas, jika penyandian tidak ditentukan, maka penyandian default platform digunakan.
axtavt
sumber
@axtavt Penjelasan yang bagus. Tetapi saya masih memiliki beberapa pertanyaan. Dalam url, ruang harus ditafsirkan sebagai %20. Jadi yang perlu kita lakukan url.replaceAll("\\+", "%20")? Dan jika itu javascript, kita seharusnya tidak menggunakan escapefungsi. Gunakan encodeURIatau encodeURIComponentsebagai gantinya. Itulah yang saya pikir.
Alston
1
@Stallman ini Java, bukan JavaScript. Bahasa yang sangat berbeda.
Charles Wood
19

Encode parery Query

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

ATAU jika Anda ingin melarikan diri karakter dalam 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
sumber
3
Menggunakan org.apache.commons.httpclient.util.URIUtiltampaknya menjadi cara paling efisien untuk menyelesaikan masalah!
Stéphane Ammar
11

Hello+Worldadalah cara browser akan menyandikan data formulir ( application/x-www-form-urlencoded) untuk GETpermintaan dan ini adalah formulir yang diterima secara umum untuk bagian permintaan URI.

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

Jika Anda mengirim permintaan ini ke servlet Java, servlet akan dengan benar mendekode nilai parameter. Biasanya satu-satunya waktu ada masalah di sini adalah jika pengkodean tidak cocok.

Sebenarnya, tidak ada persyaratan dalam spesifikasi HTTP atau URI bahwa bagian kueri yang akan dikodekan menggunakan application/x-www-form-urlencodedpasangan nilai kunci; bagian permintaan hanya perlu dalam bentuk yang diterima server web. Dalam praktiknya, ini tidak mungkin menjadi masalah.

Biasanya tidak benar menggunakan pengkodean ini untuk bagian lain dari URI (jalur misalnya). Dalam hal ini, Anda harus menggunakan skema penyandian seperti yang dijelaskan dalam RFC 3986 .

http://host/Hello%20World

Lebih lanjut di sini .

McDowell
sumber
5

Jawaban lain baik menghadirkan penggantian string manual, URLEncoder yang sebenarnya menyandikan untuk format HTML, URIUtil ditinggalkan Apache , atau menggunakan UrlEscapers Guava . Yang terakhir baik-baik saja, kecuali tidak memberikan decoder.

Apache Commons Lang menyediakan URLCodec , yang mengkodekan dan mendekode sesuai dengan format URL rfc3986 .

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

Jika Anda sudah menggunakan Spring, Anda juga dapat memilih untuk menggunakan nya kelas UriUtils .

Benny Bottema
sumber
6
URLCodec bukan solusi yang baik di sini karena ia mengkodekan spasi sebagai plus, tetapi pertanyaannya adalah meminta ruang yang akan dikodekan sebagai% 20.
davidwebster48
3

"+" benar. Jika Anda benar-benar membutuhkan% 20, ganti Plusses sendiri setelahnya.

Daniel
sumber
5
Mungkin ada masalah jika string awal benar-benar berisi karakter +.
Alexis Dufrenoy
17
@ Taroth - Tidak juga. Sebuah +karakter dalam teks asli seharusnya dikodekan sebagai %2B.
Ted Hopp
Mengatakan bahwa +itu benar tanpa mengetahui konteksnya, setidak-tidaknya, menyolok. Diturunkan. Baca jawaban lain untuk mengetahui kapan + atau% 20 akan digunakan.
Clint Eastwood
@ClintEastwood: Bisakah Anda memberi tahu saya tentang usecase karena karakter + untuk spasi tidak benar di URL? Kecuali ketika ada parser URL yang tidak sesuai di sisi lain?
Daniel
@ Danielel yakin, tidak mengatakan "salah" tetapi tidak cocok? Iya. Alat Analytics sering menggunakan params kueri dengan nilai yang dipisahkan oleh karakter tertentu, misalnya "+". Dalam hal itu, menggunakan "+" bukannya "% 20" akan salah. "+" digunakan untuk keluar dari spasi dalam formulir, sedangkan "pengodean persentase" (alias pengodean URL) lebih berorientasi ke URL.
Clint Eastwood
2

Ini berhasil untuk saya

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

Meski cukup tua, namun respon cepat:

Spring menyediakan UriUtils - dengan ini Anda dapat menentukan cara menyandikan dan bagian mana yang terkait dengan URI, mis.

encodePathSegment
encodePort
encodeFragment
encodeUriVariables
....

Saya menggunakannya karena kami sudah menggunakan Spring, yaitu tidak ada perpustakaan tambahan yang diperlukan!

Leo
sumber
0

Lihatlah kelas java.net.URI.

Fredrik Widerberg
sumber
0

Apakah saya menggunakan metode yang salah? Apa metode yang benar yang harus saya gunakan?

Ya, metode ini java.net.URLEncoder.encode tidak dibuat untuk mengonversi "" menjadi "20%" sesuai dengan spesifikasi ( sumber ).

Karakter spasi "" dikonversi menjadi tanda tambah "+".

Meskipun ini bukan metode yang benar, Anda dapat memodifikasi ini untuk: System.out.println(java.net.URLEncoder.encode("Hello World", "UTF-8").replaceAll("\\+", "%20"));have a nice day =).

Pregunton
sumber
Anda menyarankan untuk menggunakan metode yang tidak memadai ( URLEncoder.encode) dan menambalnya menggunakan replaceAllyang hanya akan bekerja dalam kasus khusus ini. Gunakan kelas dan metode yang benar sebagai gantinya, lihat jawaban lain.
pyb
@pyb sepertinya Anda tidak bisa mengerti apa yang saya tulis. Saya tidak pernah mengatakan "Saya sarankan menggunakannya", saya berkata "Anda bisa". Harap baca dan pahami sebelum Anda menulis.
Pregunton
Ini adalah situs web tanya jawab, bukan papan pesan biasa tempat orang mengobrol. Jika Anda memiliki komentar sampingan, gunakan komentar tersebut. Bicara lagi? Gunakan obrolan. Jangan memposting kode yang tidak Anda setujui sebagai jawaban. Harap baca dan pahami aturan situs ini sebelum berkontribusi dan memberi kuliah kepada orang lain.
pyb
1
Saya membatalkannya kembali karena sebagian besar solusi lain memberikan saran yang sama. Tidak ada "kasus khusus" yang diberikan untuk membuktikan metode ini salah. Menggunakan apache commons dengan blok try-catch atau dependensi terlalu merepotkan untuk metode yang dapat ditambal secara efektif dengan replaceAll.
Eugene Kartoyev
-2

GUNAKAN MyUrlEncode.URLencoding (String url, String enc) untuk menangani masalah

    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
sumber
9
menciptakan kembali roda, menambahkan kode rawan kesalahan super ke basis kode hampir selalu merupakan keputusan yang buruk.
Clint Eastwood
-6

gunakan set karakter " ISO-8859-1" untuk URLEncoder

Akhil Sikri
sumber