Delphi の URLEncode は罠が多い

# URLEncode の方法
## URLEncode は2種類ある

ちょっと古い資料を見ると

```pascal
System.Net.URLClient.TURI.URLEncode
```

を使うと出てたりします。
例えば、↓こんな感じです。

```pascal:TURI.URLEncode
program Project1;

uses
  System.Net.URLClient;

begin
  Writeln(TURI.URLEncode('Hello, Delphi!'));
  // 出力：Hello%2C%20Delphi%21
  Readln;
end.
```

で、それに従って [TURI.URLEncode](http://docwiki.embarcadero.com/Libraries/Tokyo/ja/System.Net.URLClient.TURI.URLEncode) を使うと

```console
[dcc32 警告] Project1.dpr(7): W1000 シンボル 'URLEncode' を使用することは推奨されていません : 'Use TNetEncoding.URL.Encode'
```

と、警告が出ます。
警告は消した方がいいよなーということで、警告に従って TNetEncoding.URL.Encode を使うようにしてみます。


```pascal:TNetEncoding.URL.Encode
program Project1;

uses
  System.NetEncoding;

begin
  Writeln(TNetEncoding.URL.Encode('Hello, Delphi!'));
  // 出力：Hello%2C+Delphi!
  Readln;
end.
```

警告も出ませんし、上手くいったように見えますが…
出力結果が変わっています！

```console:TURI.URLEncode
Hello%2C%20Delphi%21
```

```console:TNetEncoding.URL.Encode
Hello%2C+Delphi!
```

空白が「%20」ではなく「+」になっています。

ちなみに、Docwiki の [System.NetEncoding.TURLEncoding](http://docwiki.embarcadero.com/Libraries/Tokyo/ja/System.NetEncoding.TURLEncoding) の文章は完全に日本語がおかしく

>TURLEncoding は、スペース（プラス記号 + として）と、次の予約された URL エンコード文字のみをサポートします： ;:&=+,/?%#[]。 TURLEncoding は、プラス記号（スペースとして）と、どんなパーセント エンコード文字（%2A や %41 など）のデコードもサポートします。

意味が通じません。
日本語に直すと「空白は + 記号になる。また、;:&=+,/?%#[] 文字もエンコードされる」と書いてあります。

しかし、空白を「+」とすると正しく動かない処理もあります。

できれば、空白を「%20」にするか「+」にするか選べるといいですよね。

## 本当は2種類どころか、もっとある

先ほどの、TNetEncoding.URL.Encode は TURLEncoding.DoEncode を呼び出しますが、そこでは決め打ちで空白は「+」に変換しています。
↓こうなっています。

```pascal
else if Sp^ = ' ' then
begin
  Rp^ := '+';
  Inc(Rp)
end
```

では、どうすればいいかというと [TURLEncoding.Encode の別バージョン](http://docwiki.embarcadero.com/Libraries/Tokyo/ja/System.NetEncoding.TURLEncoding.Encode)を呼び出せばOKです。

```pascal
program Project1;

uses
  System.NetEncoding;

begin
  Writeln(TNetEncoding.URL.Encode('Hello, Delphi!', [], []));
  Readln;
end.
```

TNetEncoding.URL.Encode は overload されたメソッドがあり、上記のように引数を指定すると別バージョンの Encode メソッドが呼ばれます。
別バージョンの [TURLEncoding.Encode](http://docwiki.embarcadero.com/Libraries/Tokyo/ja/System.NetEncoding.TURLEncoding.Encode) は第2引数に追加で %xx に変更する文字、第3引数で TEncodeOption を指定できます。
なお、TEncodeOption は下記の様に定義されています。

```pascal
TEncodeOption = (SpacesAsPlus, EncodePercent);
```

* SpacesAsPlus は、空白を「+」に変換します。
* EncodeParcent は、「%」自身も変換するかどうかを示します。

今回は、空白を %20 にするだけだったので、第2, 第3引数は何も無しです。

また、先ほど「正しく動かない処理もある」と書きました。
なので、処理ごとにメソッドが用意されてればいいなあと思いますよね。
TURLEncoding には、ちゃんと用途ごとのメソッドも用意されています。
それがこちら

```pascal
function EncodePath(const APath: string; const ExtraUnsafeChars: TUnsafeChars = []): string;
function EncodeAuth(const Auth: string; const ExtraUnsafeChars: TUnsafeChars = []): string; inline;
function EncodeQuery(const AQuery: string; const ExtraUnsafeChars: TUnsafeChars = []): string; inline;
function EncodeForm(const Input: string; const ExtraUnsafeChars: TUnsafeChars = []): string; inline;
```

です。
名前から何使うか解ると思います（DocWiki にはドキュメントが存在しません…）
コレを使って、先ほどのコードは、こうも書けます。

```pascal
program Project1;

uses
  System.NetEncoding;

begin
  Writeln(TNetEncoding.URL.EncodeQuery('Hello, Delphi!'));
  Readln;
end.
```

# まとめ
いついかなる時も TNetEncoding.URL.Encode は引数を指定するか、用途ごとの Encode メソッドを使うようにしよう！