[topic] URL特殊文字のエスケープ

URL用に特殊な文字をエスケープする。

Posted feedbacks - Nested

Flatten Hidden

Rubyではcgiモジュールを使う。スペースは「+」に置換される。

1
2
require "cgi"
CGI.escape("~url quote") #=> "%7Eurl+quote"

uri でもでき、こちらのほうが柔軟です。

第二引数にマッチする文字がエンコードされます。デフォルトは URI::UNSAFE で確認できます。

1
2
3
4
5
6
7
8
9
require "uri"

p URI.escape("~url quote")
#=> "~url%20quote"
p URI.escape("~url quote", /./)
#=> "%7E%75%72%6C%20%71%75%6F%74%65"

p URI::UNSAFE
#=> /[^-_.!~*'()a-zA-Z¥d;\/?:@&=+$,\[\]]/n

Pythonではurllibを使う。スペースは%20に置換される。

1
2
import urllib
urllib.quote("~url quote") #=> '%7Eurl%20quote'

JavaScriptではescapeが使える。JavaScriptも半角スペースは%20に置き換える。

1
escape("~url quote") //=> '%7Eurl%20quote'

escape は「+」をパーセントエンコードしてくれなかったりするので受け取る側が悲しいことになります。 なので、encodeURIComponent のほうが良いかと。

1
2
3
escape("+ =&"); // => "+%20%3D%26"
encodeURI("+ =&"); // => "+%20=&"
encodeURIComponent("+ =&"); // => "%2B%20%3D%26"
C# には Uri.EscapeUriString と Uri.EscapeDataString があります。
1
2
3
4
5
6
7
8
9
using System;
static class Program {
    static void Main() {
        Console.WriteLine(Uri.EscapeUriString("~url quote/ほげ"));
            // ~url%20quote/%E3%81%BB%E3%81%92
        Console.WriteLine(Uri.EscapeDataString("~url quote/ほげ"));
            // ~url%20quote%2F%E3%81%BB%E3%81%92
    }
}

Javaだとこんな感じ

1
2
3
4
5
6
7
8
9
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
public class UrlEscape {
    public static void main(String[] args)
            throws UnsupportedEncodingException {
        String encoded = URLEncoder.encode("~url quote/ほげ", "UTF-8");
        System.out.println(encoded); // => %7Eurl+quote%2F%E3%81%BB%E3%81%92
    }
}

PHPです

1
<?= urlencode("^?~url quote/ほげ"); // => %7F%7Eurl+quote%2F%E3%81%BB%E3%81%92 ?>
CGIwithRというライブラリーにアンエスケープの関数はあったのですが、
エスケープする関数はないようです。

> escape("~url quote/ほげ")
[1] "%7eurl%20quote%2f%82%d9%82%b0"
1
2
3
4
5
escape <- function(str){
    paste(sapply(unlist(strsplit(str, "")),
                 function(s){ifelse((regexpr("\\W", s, perl=TRUE)[1] > 0),
                                    paste("%", charToRaw(s), collapse="", sep=""), s)}), collapse="")
}

Gaucheではrfc.uriモジュールを使います。

1
2
3
4
gosh> (use rfc.uri)
#<undef>
gosh> (uri-encode-string "~url quote/ほげ")
"~url%20quote%2f%c2%a4%db%a4%c2%b2"

ちなみに、URI中でエスケープしなくて良い文字はRFC2396とRFC3986で若干異なっています。rfc.uriモジュールはRFC3986に従っていますが、キーワード引数を与えることで変えることが可能です。

1
2
3
4
gosh> (uri-encode-string "!(foo*)")
"%21%28foo%2a%29"
gosh> (uri-encode-string "!(foo*)" :noescape *rfc2396-unreserved-char-set*)
"!(foo*)"
Squeak Smalltalk では、文字列に encodeForHTTP を送信します。
1
'~url quote' encodeForHTTP   "=> '%7Eurl%20quote' "

libcurl を使う場合は curl_easy_escape を使ってエスケープできます。 [a-zA-Z0-9] 以外がエスケープされるようです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdlib.h>
#include <stdio.h>
#include "curl/curl.h"

int main( int argc, char *argv[] ){
   CURL *curl;
   char *escaped;

   if( argc < 2 ){
      fprintf(stderr, "usage: %s url\n", argv[0] );
      return EXIT_FAILURE;
   }
   if( !(curl = curl_easy_init()) ){
      fprintf(stderr, "failed to init curl\n");
      return EXIT_FAILURE;
   }
   escaped = curl_easy_escape( curl, argv[1], 0 );
   curl_free( curl );

   if( !escaped ){
      fprintf(stderr, "failed to escape\n");
      return EXIT_FAILURE;
   }
   printf("%s\n", escaped );
   free( escaped );
   return EXIT_SUCCESS;
}
Network.URIモジュールの escapeURIString を使います.このモジュールは,
RFC3986に準拠しています.
ewcapeURIString の型は (Char -> Bool) -> String -> String で,
第一引数にEscapeしなくてよい文字を判定する述語を渡します.


*Main> mapM_ putStrLn $ [ escape p x | p <- [isUnreserved,isAllowedInURI,isUnescapedInURI], x <- [sample0, sample1]]
~url%20quote%2F%E3%81%BB%E3%81%92
%21%28foo%2A%29
~url%20quote/%E3%81%BB%E3%81%92
!(foo*)
~url%20quote/%E3%81%BB%E3%81%92
!(foo*)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import Network.URI
import Codec.Binary.UTF8.String
import qualified System.IO.UTF8 as U

escape p = escapeURIString p . encodeString

unescape = decodeString . unEscapeString

sample0 = "~url quote/ほげ"
sample1 = "!(foo*)"
cURLpp を使えば escape, unescape が用意されています。 curl が元になっているので [a-zA-Z0-9] 以外がエスケープされるようです。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include <cstdlib>
#include <iostream>
#include <curlpp/cURLpp.hpp>

using namespace std;
using namespace cURLpp;

int main ( int argc, char *argv[] ){
   if( argc < 2 ){
      return EXIT_FAILURE;
   }
   string escaped = cURLpp::escape( argv[1] );
   cout << escaped << endl;
   string unescaped = cURLpp::unescape( escaped );
   cout << unescaped << endl;
   return EXIT_SUCCESS;
}

ログイン忘れてました…。だけではなんなので、 少しだけ書き方を変えて短くしたものを。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#include <cstdlib>
#include <iostream>
#include <curlpp/cURLpp.hpp>

using namespace std;

int main ( int argc, char *argv[] ){
   if( argc < 2 ){
      return EXIT_FAILURE;
   }
   string escaped, unescaped;
   cout << (escaped = cURLpp::escape( argv[1] )) << endl;
   cout << (unescaped = cURLpp::unescape( escaped )) << endl;
   return EXIT_SUCCESS;
}

Ocamlnetのほうは空白をどちらに変換するか選択できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
(*Ocamlnetのnetstring
  ocaml -I +pcre -I +netsys -I +netstring  \
    unix.cma pcre.cma netsys.cma netstring.cma*)
    
  open Netencoding;;
(* ' ' <-> '+' *)
  Url.encode "~url quote";;
  Url.decode "%7Eurl+quote";;
(* ' ' <-> '%20' *)
  Url.encode ~plus:false "~url quote" ;;
  Url.decode ~plus:false "%7Eurl%20quote";;


(*libcurl-ocaml(ocurl?)
  ocaml -I +curl curl.cma*)
  Curl.escape "~url quote";; 
  Curl.unescape "%7Eurl%20quote";;

Index

Feed

Other

Link

Pathtraq

loading...