URLEncoderがスペース文字を変換できない


179

私は期待している

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

出力する:

Hello%20World

(20はスペースのASCII 16進コードです)

しかし、私が得るものは:

Hello+World

間違った方法を使用していますか?私が使用すべき正しい方法は何ですか?


3
クラス名は確かに紛らわしく、多くの人がそれを誤って使用しています。ただし、URLDecoderが適用されると元の値が復元されるため、ユーザーはそれを認識しません。そのため、+または%20は実際には関係ありません。
評判の悪い2011年

回答:


227

これは期待どおりに動作します。はURLEncoder、HTMLフォームでURLをエンコードする方法のHTML仕様を実装します。

以下からのJavadoc

このクラスには、文字列をapplication / x-www-form-urlencoded MIME形式に変換するための静的メソッドが含まれています。

そしてHTML仕様から:

application / x-www-form-urlencoded

このコンテンツタイプで送信されるフォームは、次のようにエンコードする必要があります。

  1. コントロールの名前と値はエスケープされます。スペース文字は「+」に置き換えられます

あなたはそれを交換する必要があります、例えば:

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

19
まあこれは確かに答えです。置き換えるのではなく、タスクを実行するためのJavaライブラリまたは関数がありませんか?
co2f2e 2013

5
プラス記号はエスケープする必要がありますt.println(java.net.URLEncoder.encode("Hello World", "UTF-8").replace("\\+", "%20"));
ジョージ

26
@congliuは正しくありません-おそらく正規表現で機能するreplaceAll()を考えているでしょう-replace()は単純な文字シーケンスの置換です。
CupawnTae 2013

12
はい@congliuは良い方法です:URLEncoder.encode( "Myurl"、 "utf-8")。replaceAll( "\\ +"、 "%20");
2013年

9
@ClintEastwoodこの回答は、java.net.URLEncoderを使用することを奨励しています。そして、この答えは、その上にreplace()を使用したパッチを提案します。何故なの?このソリューションはバグが発生しやすく、他の20の同様の質問につながる可能性がありますが、性質が異なります。これが先見の明があると私が言った理由です。
pyb

57

スペースは%20、URLおよび+フォーム送信データ(コンテンツタイプapplication / x-www-form-urlencoded)にエンコードされます。前者が必要です。

Guavaの使用:

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を使用しないでください。これはスペースをエンコードするだけです。代わりにライブラリを使用してください。


Android、com.google.guava:guava:22.0-rc1-androidでも機能します。
Bevor

1
@Bevor rc1は、最初のリリース候補、つまり一般リリースがまだ承認されていないバージョンを意味します。可能であれば、バグが含まれていることが知られているスナップショット、アルファ、ベータ、rcのないバージョンを選択してください。
pyb

1
@pybありがとうございます。ただし、プロジェクトが完了したらライブラリを更新します。つまり、私は最終版なしでは製品版に行きません。それでもまだ何週間もかかるので、最終版はあると思います。
2017年

1
残念ながら、ApacheのURLCodecとは異なり、Guavaにはデコーダーがありません。
ベニーボッテマ2018年

26

このクラスは、実行application/x-www-form-urlencoded従って置き換え、型コードではなくパーセントエンコーディングを有することは+正しい動作です。

javadocから:

文字列をエンコードするときは、次のルールが適用されます。

  • 英数字の「a」から「z」、「A」から「Z」、および「0」から「9」は変わりません。
  • 特殊文字「。」、「-」、「*」、および「_」は同じままです。
  • スペース文字「」はプラス記号「+」に変換されます。
  • 他のすべての文字は安全ではなく、いくつかのエンコードスキームを使用して最初に1バイト以上に変換されます。次に、各バイトは3文字の文字列「%xy」で表されます。ここで、xyはバイトの2桁の16進数表現です。使用が推奨されるコード化スキームはUTF-8です。ただし、互換性の理由から、エンコーディングが指定されていない場合は、プラットフォームのデフォルトのエンコーディングが使用されます。

@axtavtいい説明。しかし、まだいくつか質問があります。ではurl、スペースがあると解釈されなければなりません%20。だから私たちはする必要がありurl.replaceAll("\\+", "%20")ますか?また、JavaScriptの場合は、escape関数を使用しないでください。encodeURIまたはencodeURIComponent代わりに使用します。私もそう思っていました。
Alston

1
@StallmanこれはJavaScriptではなくJavaです。まったく異なる言語。
Charles Wood

19

クエリパラメータのエンコード

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

3
を使用org.apache.commons.httpclient.util.URIUtilすることが問題を解決する最も効率的な方法のようです!
ステファン・アマル

11

Hello+Worldブラウザがリクエストのフォームデータ(application/x-www-form-urlencoded)をエンコードする方法です。GETこれは、URIのクエリ部分で一般的に受け入れられているフォームです。

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

このリクエストをJavaサーブレットに送信した場合、サーブレットはパラメータ値を正しくデコードします。通常、ここで問題が発生するのは、エンコーディングが一致しない場合のみです。

厳密に言えば、HTTPやURIの仕様では、application/x-www-form-urlencodedキーと値のペアを使用してクエリ部分をエンコードする必要はありません。クエリ部分は、Webサーバーが受け入れる形式である必要があります。実際には、これが問題になることはほとんどありません。

URIの他の部分(たとえばパス)にこのエンコーディングを使用することは、一般的に正しくありません。その場合は、RFC 3986で説明されいるエンコードスキームを使用する必要があります。

http://host/Hello%20World

詳細はこちら


5

もう1つの答えは、手動の文字列置換、HTML形式に実際にエンコードするURLEncoder、Apacheの放棄された URIUtil、またはGuavaのUrlEscapersの使用のいずれかです。最後のものは問題ありませんが、デコーダを提供していません。

Apache Commons LangはURLCodecを提供します。これは URL形式rfc3986に従ってエンコードおよびデコードします。

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

すでにSpringを使用 ている場合は、そのUriUtilsクラスを使用することもできます。


6
URLCodecは、スペースをプラスとしてエンコードするため、ここでは適切なソリューションではありませんが、問題は、スペースを%20としてエンコードすることを求めています。
davidwebster48

3

「+」は正しいです。本当に%20が必要な場合は、後で自分でPlussesを交換してください。


5
最初の文字列に実際に+文字が含まれていた場合、問題が発生する可能性があります。
Alexis Dufrenoy 2013年

17
@Traroth-そうではない。+元のテキスト内の文字は、次のようにコード化されることになっています%2B
テッドホップ2013

+コンテキストを知らなくてもそれが正しいと言うことは、少なくとも知識を深めることです。反対票。+または%20をいつ使用するかについては、他の回答を読んでください。
クリントイーストウッド

@ClintEastwood:スペースの+文字がURLで正しくないというユースケースについて教えてください。反対側に非準拠のURLパーサーがある場合を除いて?
ダニエル

@Daniel確かに、「正しくない」と言っていませんが、不適切ですか。はい。分析ツールでは、「+」などの特定の文字で区切られた値を持つクエリパラメータがよく使用されます。その場合、「%20」の代わりに「+」を使用するのは間違っています。「+」はフォーム内のスペースをエスケープするために使用されますが、「パーセンテージエンコーディング」(別名URLエンコーディング)はURLを指向しています。
クリントイーストウッド


2

これは私のために働いた

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

1

かなり古いですが、それでも迅速な対応:

SpringはUriUtilsを提供します-これにより、エンコードする方法と、URIに関連する部分を指定できます。

encodePathSegment
encodePort
encodeFragment
encodeUriVariables
....

私はすでにSpringを使用しているため、これらを使用しています。つまり、追加のライブラリは必要ありません。



0

間違った方法を使用していますか?私が使用すべき正しい方法は何ですか?

はい、このメソッドjava.net.URLEncoder.encodeは、仕様(ソース)に従って「」を「20%」に変換するために作成されていません。

スペース文字「」はプラス記号「+」に変換されます。

これは正しい方法ではありませんが、これを次のSystem.out.println(java.net.URLEncoder.encode("Hello World", "UTF-8").replaceAll("\\+", "%20"));ように変更できます。


適切でない方法(URLEncoder.encode)を使用replaceAllし、この特定のケースでのみ機能するパッチを使用することをお勧めします。代わりに正しいクラスとメソッドを使用してください。他の回答を参照してください。
pyb 2017

@pybは私が書いたものを理解できないようです。「使用することをお勧めします」と言ったことは一度もありませんが、「できる」と言いました。書く前に読んで理解してください。
プレガントン2017

これは質疑応答のWebサイトであり、人々がチャットする通常の掲示板ではありません。サイドコメントがある場合は、コメントを使用します。もっと長い話?チャットを使用してください。同意できないコードを回答として投稿しないでください。他の人に寄稿したり講演したりする前に、このサイトのルールを読んで理解してください。
pyb 2017

1
他のほとんどのソリューションが同じアドバイスを提供するので、私はそれを支持します。この方法が間違っていることを証明する「特定のケース」は提供されていません。try-catchブロックまたは依存関係でApache Commonsを使用することは、replaceAllを使用して効果的にパッチを適用できるメソッドにとっては面倒です。
Eugene Kartoyev

-2

使用MyUrlEncode.URLencoding(文字列の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();
    }

}

9
ホイールを再発明し、コードベースにエラーが発生しやすいコードを追加することは、ほとんどの場合悪い決定です。
クリントイーストウッド

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.