ここでは、文字列をURLエンコード(パーセントエンコーディング、URLエスケープ、URL符号化、パーセント符号化、百分率符号化)する方法と、URLデコードする方法を紹介します。
HttpUtilityクラス(System.Web名前空間)のUrlEncodeメソッドを使ってURLエンコードを行うことができます。HttpUtilityクラスを使用するには、System.Web.dllを参照設定に追加する必要があります。
以下に、HttpUtility.UrlEncodeメソッドが文字列をどのようにエンコードするかを調べるコードを示します。
'URLエンコードする文字列 Dim str As String = "!()_-*.aA0 ?#$%&|@\/[]{}<>+=^~""'`;:,あ" 'URLエンコードする Dim urlEnc As String = System.Web.HttpUtility.UrlEncode(str) '結果を表示する Console.WriteLine(urlEnc) '!()_-*.aA0+%3f%23%24%25%26%7c%40%5c%2f%5b%5d%7b%7d%3c%3e%2b%3d%5e '%7e%22%27%60%3b%3a%2c%e3%81%82
//URLエンコードする文字列 string str = "!()_-*.aA0 ?#$%&|@\\/[]{}<>+=^~\"'`;:,あ"; //URLエンコードする string urlEnc = System.Web.HttpUtility.UrlEncode(str); //結果を表示する Console.WriteLine(urlEnc); //!()_-*.aA0+%3f%23%24%25%26%7c%40%5c%2f%5b%5d%7b%7d%3c%3e%2b%3d%5e //%7e%22%27%60%3b%3a%2c%e3%81%82
このように、UrlEncodeメソッドがエンコードするのは、英字(大文字・小文字)、数字、「!」(感嘆符)、「(」、「)」(丸かっこ)、「_」(アンダースコア)、「-」(ハイフン)、「*」(アスタリスク)、「.」(ピリオド)以外の文字です。
また、半角スペースを「+」にエンコードします。この点は、「application/x-www-form-urlencoded」に従っています。
16進数表現は、小文字です。
UrlEncodeメソッドでは、文字コードを指定しないとUTF-8としてエンコードします。文字コードを指定する時は、2番目のパラメータにEncodingオブジェクトを指定します。または、「文字列を文字コードを指定してバイト型配列のデータに変換する」のように文字列をバイト型配列に変換してからUrlEncodeメソッドでURLエンコードすることもできます。
以下の例では、Shift-JISでエンコードしています。
Dim str As String = "あいうえお+- &!" 'Shift-JISでURLエンコードする Dim enc As System.Text.Encoding = System.Text.Encoding.GetEncoding("shift_jis") Dim urlEncSjis As String = System.Web.HttpUtility.UrlEncode(str, enc) Console.WriteLine(urlEncSjis) '%82%a0%82%a2%82%a4%82%a6%82%a8%2b-+%26!
string str = "あいうえお+- &!"; //Shift-JISでURLエンコードする System.Text.Encoding enc = System.Text.Encoding.GetEncoding("shift_jis"); string urlEncSjis = System.Web.HttpUtility.UrlEncode(str, enc); Console.WriteLine(urlEncSjis); //%82%a0%82%a2%82%a4%82%a6%82%a8%2b-+%26!
.NET Framework 2.0以降では、Uri.EscapeDataStringとUri.EscapeUriStringメソッドでもURLエンコードができます。まずはこれらのメソッドがどのようにエンコードするか、次のようなコードで確かめてみましょう。
注意:後ほど詳しく説明しますが、これらのメソッドのデフォルトでのエンコードの仕方は、.NET Framework 4.0以前と4.5以降で異なります。ここでの説明は4.5以降についてです。
Dim str As String = "!()_-*.aA0 ?#$%&|@\/[]{}<>+=^~""'`;:,あ" 'EscapeDataStringでURLエンコードする Dim escapeDataString As String = Uri.EscapeDataString(str) Console.WriteLine(escapeDataString) '%21%28%29_-%2A.aA0%20%3F%23%24%25%26%7C%40%5C%2F%5B%5D%7B%7D%3C%3E%2B '%3D%5E~%22%27%60%3B%3A%2C%E3%81%82 'EscapeUriStringでURLエンコードする Dim escapeUriString As String = Uri.EscapeUriString(str) Console.WriteLine(escapeUriString) '!()_-*.aA0%20?#$%25&%7C@%5C/[]%7B%7D%3C%3E+=%5E~%22'%60;:,%E3%81%82
string str = "!()_-*.aA0 ?#$%&|@\\/[]{}<>+=^~\"'`;:,あ"; //EscapeDataStringでURLエンコードする string escapeDataString = Uri.EscapeDataString(str); Console.WriteLine(escapeDataString); //%21%28%29_-%2A.aA0%20%3F%23%24%25%26%7C%40%5C%2F%5B%5D%7B%7D%3C%3E%2B //%3D%5E~%22%27%60%3B%3A%2C%E3%81%82 //EscapeUriStringでURLエンコードする string escapeUriString = Uri.EscapeUriString(str); Console.WriteLine(escapeUriString); //!()_-*.aA0%20?#$%25&%7C@%5C/[]%7B%7D%3C%3E+=%5E~%22'%60;:,%E3%81%82
この結果を見ると、両者とも半角スペースを「%20」にエンコードしています。これは、RFC3986に従ったエンコード方法です。16進数表現は大文字です。文字コードはUTF-8です。
EscapeDataStringメソッドは、英字(大文字・小文字)と数字以外に、「_」(アンダースコア)、「-」(ハイフン)、「.」(ピリオド)、「~」(チルダ)をエンコードしせん。つまり、RFC3986の非予約文字(Unreserved Characters)以外をエンコードしています。
EscapeUriStringメソッドはそれに加えて、「!」、「(」、「)」、「*」、「.」、「?」、「#」、「$」、「&」、「@」、「/」、「[」、「]」、「+」、「=」、「'」、「;」、「:」、「,」をエンコードしません。つまり、RFC3986の非予約文字と予約文字(Reserved Characters)以外をエンコードします。予約文字は、URLの区切りなどの特別な意味を持っている文字ですので、URLの基本的な構造を崩さずにURLエンコードしたい時は、EscapeUriStringメソッドを使います。一方、URL全体をURLエンコードしてクエリー文字列を作成する時には、使用できません。
よって、基本的には、RFC3986に基づいたURLエンコードを行うには、EscapeDataStringメソッドを使います。特別な事情で予約文字をエンコードしてはいけない場合のみ、EscapeUriStringメソッドを使います。
上記のEscapeUriStringとEscapeDataStringメソッドの説明は.NET Framework 4.5以降のもので、4.0以前では異なります。4.0以前では、RFC2396に準拠した方法でエンコードを行います。つまり、EscapeDataStringメソッドはRFC2396の非予約文字(英字数字と「-_.!~*'()」)以外をエンコードし、EscapeUriStringメソッドはRFC2396の非予約文字と予約文字(「;/?:@&=+$,」)以外をエンコードします。
4.0と4.5のEscapeDataStringメソッドを比べると、4.5では「!()*'」をエンコードします。4.0と4.5のEscapeUriStringメソッドを比べると、4.5では「[]」をエンコードしません。
MSDNのEscapeUriStringとEscapeDataStringメソッドの説明によると、国際化リソース識別子(IRI)または国際化ドメイン名(IDN)解析が有効になっている場合は、RFC3986およびRFC3987に従って文字列がエスケープされ、無効になっている場合は、RFC2396に従ってエスケープされるそうです。IRIのサポートは.NET Framework 2.0 SP1から提供されましたが、IRI解析がデフォルトで有効なのは4.5からで、それより前では無効になっています(「.NET Framework 4.5 のアプリケーションの互換性」より)。そのため、.NET 4.0と4.5でこのような違いが出るのだろうと思われます。
.NET 4.0以前でIRI解析を有効にするには、app.config(または、machine.config)ファイルに設定を追加します。この方法は、「ドメイン名(ホスト名)をPunycodeに変換(エンコード、デコード)する」で説明しています。しかし、私が試した限りでは、IRIを有効にしてもEscapeUriStringとEscapeDataStringメソッドはRFC2396に従ったエンコードしかしませんでした。
Uri.EscapeDataStringとEscapeUriStringを使わずにRFC3986に基づいたURLエンコードを行うコードを書いてみました。蛇足として、UTF-8以外の文字コードも指定できるようにしています。
Public Class RFC3986Uri Public Shared ReadOnly UnreservedCharacters As String = "-._~" Public Shared ReadOnly ReservedCharacters As String = _ UnreservedCharacters + ":/?#[]@!$&'()*+,;=" ''' <summary> ''' RFC3986に基づいてURLエンコードを行います。 ''' </summary> ''' <param name="stringToEscape"> ''' URLエンコードする文字列。 ''' </param> ''' <param name="escapeEncoding"> ''' エンコード方式を指定するEncoding オブジェクト。 ''' </param> ''' <returns> ''' URLエンコードされた文字列。 ''' </returns> Public Shared Function EscapeDataString( _ ByVal stringToEscape As String, _ ByVal escapeEncoding As System.Text.Encoding) As String Return PercentEncodeString( _ stringToEscape, UnreservedCharacters, escapeEncoding) End Function Public Shared Function EscapeDataString( _ ByVal stringToEscape As String) As String Return EscapeDataString(stringToEscape, System.Text.Encoding.UTF8) End Function ''' <summary> ''' RFC3986に基づいてURI文字列のURLエンコードを行います。 ''' </summary> ''' <param name="stringToEscape"> ''' URLエンコードする文字列。 ''' </param> ''' <param name="escapeEncoding"> ''' エンコード方式を指定するEncoding オブジェクト。 ''' </param> ''' <returns> ''' URLエンコードされた文字列。 ''' </returns> Public Shared Function EscapeUriString( _ ByVal stringToEscape As String, _ ByVal escapeEncoding As System.Text.Encoding) As String Return PercentEncodeString( _ stringToEscape, ReservedCharacters, escapeEncoding) End Function Public Shared Function EscapeUriString( _ ByVal stringToEscape As String) As String Return EscapeUriString(stringToEscape, System.Text.Encoding.UTF8) End Function Friend Shared Function PercentEncodeString( _ ByVal stringToEscape As String, _ ByVal dontEscapeCharacters As String, _ ByVal escapeEncoding As System.Text.Encoding) As String Dim encodedString As New System.Text.StringBuilder() For Each c As Char In stringToEscape If ("0"c <= c AndAlso c <= "9"c) OrElse _ ("a"c <= c AndAlso c <= "z"c) OrElse _ ("A"c <= c AndAlso c <= "Z"c) OrElse _ (0 <= dontEscapeCharacters.IndexOf(c)) Then 'エンコードしない文字の場合 encodedString.Append(c) Else 'エンコードする文字の場合 encodedString.Append(HexEscape(c, escapeEncoding)) End If Next Return encodedString.ToString() End Function ''' <summary> ''' 指定した文字のパーセントエンコーディング(百分率符号化)を行います。 ''' </summary> ''' <param name="character"> ''' パーセントエンコーディングする文字。 ''' </param> ''' <param name="escapeEncoding"> ''' エンコード方式を指定するEncoding オブジェクト。 ''' </param> ''' <returns> ''' パーセントエンコーディングされた文字列。 ''' </returns> Public Shared Function HexEscape( _ ByVal character As Char, _ ByVal escapeEncoding As System.Text.Encoding) As String If 255 < AscW(character) Then 'characterが255を超えるときはUri.HexEscapeが使えない Dim buf As New System.Text.StringBuilder() Dim characterBytes As Byte() = _ escapeEncoding.GetBytes(character.ToString()) For Each b As Byte In characterBytes buf.AppendFormat("%{0:X2}", b) Next Return buf.ToString() End If Return Uri.HexEscape(character) End Function End Class
public class RFC3986Uri { public static readonly string UnreservedCharacters = "-._~"; public static readonly string ReservedCharacters = UnreservedCharacters + ":/?#[]@!$&'()*+,;="; /// <summary> /// RFC3986に基づいてURLエンコードを行います。 /// </summary> /// <param name="stringToEscape"> /// URLエンコードする文字列。 /// </param> /// <param name="escapeEncoding"> /// エンコード方式を指定するEncoding オブジェクト。 /// </param> /// <returns> /// URLエンコードされた文字列。 /// </returns> public static string EscapeDataString(string stringToEscape, System.Text.Encoding escapeEncoding) { return PercentEncodeString( stringToEscape, UnreservedCharacters, escapeEncoding); } public static string EscapeDataString(string stringToEscape) { return EscapeDataString(stringToEscape, System.Text.Encoding.UTF8); } /// <summary> /// RFC3986に基づいてURI文字列のURLエンコードを行います。 /// </summary> /// <param name="stringToEscape"> /// URLエンコードする文字列。 /// </param> /// <param name="escapeEncoding"> /// エンコード方式を指定するEncoding オブジェクト。 /// </param> /// <returns> /// URLエンコードされた文字列。 /// </returns> public static string EscapeUriString(string stringToEscape, System.Text.Encoding escapeEncoding) { return PercentEncodeString( stringToEscape, ReservedCharacters, escapeEncoding); } public static string EscapeUriString(string stringToEscape) { return EscapeUriString(stringToEscape, System.Text.Encoding.UTF8); } internal static string PercentEncodeString(string stringToEscape, string dontEscapeCharacters, System.Text.Encoding escapeEncoding) { System.Text.StringBuilder encodedString = new System.Text.StringBuilder(); foreach (char c in stringToEscape) { if (('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || (0 <= dontEscapeCharacters.IndexOf(c))) { //エンコードしない文字の場合 encodedString.Append(c); } else { //エンコードする文字の場合 encodedString.Append(HexEscape(c, escapeEncoding)); } } return encodedString.ToString(); } /// <summary> /// 指定した文字のパーセントエンコーディング(百分率符号化)を行います。 /// </summary> /// <param name="character"> /// パーセントエンコーディングする文字。 /// </param> /// <param name="escapeEncoding"> /// エンコード方式を指定するEncoding オブジェクト。 /// </param> /// <returns> /// パーセントエンコーディングされた文字列。 /// </returns> public static string HexEscape(char character, System.Text.Encoding escapeEncoding) { if (255 < (int)character) { //characterが255を超えるときはUri.HexEscapeが使えない System.Text.StringBuilder buf = new System.Text.StringBuilder(); byte[] characterBytes = escapeEncoding.GetBytes(character.ToString()); foreach (byte b in characterBytes) { buf.AppendFormat("%{0:X2}", b); } return buf.ToString(); } return Uri.HexEscape(character); } }
以上の説明をまとめます。
URLデコードを行うには、HttpUtilityクラスのUrlDecodeメソッドを使います。HttpUtilityクラスを使用するには、System.Web.dllを参照設定に追加する必要があります。
以下にUrlDecodeメソッドを使用した例を示します。
Dim urlEnc As String = "%2b%2d%28%20+%29%e3%81%82" 'URLデコードする Dim urlDec As String = System.Web.HttpUtility.UrlDecode(urlEnc) Console.WriteLine(urlDec) '「+-( )あ」と表示される
string urlEnc = "%2b%2d%28%20+%29%e3%81%82"; //URLデコードする string urlDec = System.Web.HttpUtility.UrlDecode(urlEnc); Console.WriteLine(urlDec); //「+-( )あ」と表示される
UrlDecodeメソッドは、文字コードを指定しないとUTF-8としてデコードします。文字コードを指定する時は、2番目のパラメータにEncodingオブジェクトを指定します。
Dim urlEncSjis As String = "%2b%2d%28%20%29%82%a0" 'Shift-JISでURLデコードする Dim enc As System.Text.Encoding = System.Text.Encoding.GetEncoding("shift_jis") Dim urlDecSjis As String = System.Web.HttpUtility.UrlDecode(urlEncSjis, enc) Console.WriteLine(urlDecSjis) '「+-( )あ」と表示される
string urlEncSjis = "%2b%2d%28%20%29%82%a0"; //Shift-JISでURLデコードする System.Text.Encoding enc = System.Text.Encoding.GetEncoding("shift_jis"); string urlDecSjis = System.Web.HttpUtility.UrlDecode(urlEncSjis, enc); Console.WriteLine(urlDecSjis); //「+-( )あ」と表示される
.NET Framework 2.0以降では、Uri.UnescapeDataStringメソッドを使ってURLデコードすることもできます。ただし、このメソッドは「+」を半角スペースに変換しませんので、注意が必要です。
また、文字コードはUTF-8にだけ対応しています。
以下にUnescapeDataStringメソッドを使った例を示します。
Dim urlEnc As String = "%2b%2d%28%20+%29%e3%81%82" 'URLデコードする Dim urlDec As String = Uri.UnescapeDataString(urlEnc) Console.WriteLine(urlDec) '「+-( +)あ」と表示される '「+」が半角スペースにデコードされるようにするには、次のようにする Dim urlDec2 As String = Uri.UnescapeDataString(urlEnc.Replace("+"c, " "c)) Console.WriteLine(urlDec2) '「+-( )あ」と表示される
string urlEnc = "%2b%2d%28%20+%29%e3%81%82"; //URLデコードする string urlDec = Uri.UnescapeDataString(urlEnc); Console.WriteLine(urlDec); //「+-( +)あ」と表示される //「+」が半角スペースにデコードされるようにするには、次のようにする string urlDec2 = Uri.UnescapeDataString(urlEnc.Replace('+', ' ')); Console.WriteLine(urlDec2); //「+-( )あ」と表示される
以上の説明をまとめます。
HttpServerUtilityクラスのインスタンスメソッドであるUrlEncodeメソッドとUrlDecodeメソッドでもURLエンコードとデコードができます。これらのメソッドは内部でHttpUtilityクラスのUrlEncodeメソッドとUrlDecodeメソッドを呼び出していますので、結果はHttpUtilityクラスを使った時と同じです。
.NET Framework 1.1以降で使用できるHttpUtility.UrlPathEncodeメソッドは、非ASCII文字と半角スペースだけをエンコードします。半角スペースは「%20」にエンコードします。また、エンコードする文字列に「?」が含まれる時、「?」より前にある文字列だけがエンコードされます。
Dim str As String = "http://localhost/<文字> /f.cgi?w=文字" Console.WriteLine(System.Web.HttpUtility.UrlPathEncode(str)) '「http://localhost/<%e6%96%87%e5%ad%97>%20/f.cgi?w=文字」と表示される
string str = "http://localhost/<文字> /f.cgi?w=文字"; Console.WriteLine(System.Web.HttpUtility.UrlPathEncode(str)); //「http://localhost/<%e6%96%87%e5%ad%97>%20/f.cgi?w=文字」と表示される
HttpUtility.UrlEncodeUnicodeメソッドは、Unicode文字列("%u"で始まる4桁の16進数)にエンコードします。しかしこのメソッドは.NET Framework 4.5からObsolete属性が適用され、使うべきではないとされました。
Dim str As String = "あいうえお- &!" Console.WriteLine(System.Web.HttpUtility.UrlEncodeUnicode(str)) '「%u3042%u3044%u3046%u3048%u304a-+%26!」と表示される
string str = "あいうえお- &!"; Console.WriteLine(System.Web.HttpUtility.UrlEncodeUnicode(str)); //「%u3042%u3044%u3046%u3048%u304a-+%26!」と表示される