/
423.txt
1069 lines (794 loc) · 56.6 KB
/
423.txt
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
[37] [[URL]] で用いられている、 [CODE(char)[[[%]]]] と2桁の[[十六進数]]によって[[オクテット]]あるいは[[文字]]を表す表記法を[DFN[[RUBYB[パーセント符号化]@en[percent-encoding]]]]といいます。
;; [38] 以前は [DFN[URI [RUBYB[エスケープ]@en[escape]]]]、
[DFN[URL [RUBY[符号化][エンコーディング]@en[encoding]]]]などとも呼ばれていました。
;; [66] [[文字列]]を[[パーセント符号化]]したり、[[パーセント符号化]]された[[文字列]]を[[復号]]したりするには、
[[Charinfo]] をお使いください:
[REFS[
- [65] [CITE@en[Charinfo — ""]] ([TIME[2015-04-12 18:14:40 +09:00]] 版) <https://chars.suikawiki.org/string>
]REFS]
* 古い情報の見分け方
[182] [[パーセント符号化]]をめぐっては、古い情報や誤った情報が蔓延しています。
権威のありそうな文書を引用したり、さも当然のように断定したりしているのが、
事実とまったく正反対の説明だったりもするので、注意しなければなりません。
[183] 次のような条件に当てはまるものは、'''誤った情報の可能性が高い'''です。
- 古い[[仕様書]]に言及: 「[[RFC]]」 「[[IETF]]」 「[[W3C]]」
- 古い用語で説明: 「[[URLエンコーディング]]」「[[URIエスケープ]]」「[[URI符号化]]」「[[IRI]]」
- [[URL]] と[[フォームデータ]]を混同している
- 「[VAR[○○]] (有名実装) は現在の仕様に合っていない」と断定
* 仕様書
[REFS[
- [43] '''[CITE@en-US[URL Standard]] ([TIME[2014-04-15 13:16:21 +09:00]] 版) <https://url.spec.whatwg.org/#percent-encoded-bytes>'''
- [144] [CITE[ECMAScript® 2017 Language Specification]] ([TIME[2016-06-04 05:39:35 +09:00]]) <https://tc39.github.io/ecma262/#sec-uri-handling-functions>
- [26] [CITE@en[RFC 5849 - The OAuth 1.0 Protocol]] ([TIME[2014-12-28 14:19:21 +09:00]] 版) <http://tools.ietf.org/html/rfc5849#section-3.6>
]REFS]
[27] [[URL]] および [CODE(MIME)@en[[[application/x-www-form-urlencoded]]]]
における[[パーセント符号化]]は、 [[URL Standard]] で規定されています。
[28] [[URL]] および関連する概念が厳密に規定されてこなかった歴史的経緯から、
それ以外のいくつかの仕様書にも類似した規定が含まれています。
* 呼称
[44] [[パーセント符号化]]は、歴史的に [DFN[[RUBYB[URI 符号化] [URI encoding]]]]、
[DFN[[RUBYB[URI 逃避符号化] [URI escape(d) encoding]]]]、
[DFN[URI [RUBY[逃避] [エスケープ] [escape]]]]など色々な呼ばれ方をしてきました。
[ABBR[[[URI]]] [統一資源識別子]] の第4次規格である [[RFC 3986]] ([[IETF]]
[[インターネット標準]]) が以前の用語にかえて[DFN[[RUBYB[パーセント符号化] [percent‐encoding]]]]という用語を採用したため、
現行仕様である [[WHATWG]] [[URL Standard]] もそれを踏襲しています [SRC[>>43]]。
[HISTORY[
[45] [CODE(URI)[%[VAR[HH]]]] という表記のことを、 UR[IL] escape という流儀と UR[IL] encode という流儀があります。仕様書にも混在していて、有意な使い分けも認められません。早い話がどっちでもいいということです。
[46] あ、でも、最新の URI の定義である [[RFC 2396]] では整理されていて、
URI 符号化の意味の encode は必ず [Q[escaped encoding]]
という語で登場します。 [Q[octet encoding]] と対になっています。
[47] 2396 に「URI encoding」という語がないことからこれを誤りで「URI escape」が正しいとする論もありますけどナンセンスですね。正確を期すなら「URI escape encoding」とフルスペって初めて意味を成す。 URI escape encoding にするという意味の動詞なら「URI escape する」でもいいとは思いますがね。
[76] 俗に、[[エスケープシーケンス]]と呼ぶこともあります。
]HISTORY]
* 構文
[97] [[パーセント符号化]]では、 [CODE[[[%]]]] と二桁の [[ASCII]]
[[十六進数字]]によって一つの[[オクテット]]を表します。 [CODE[[[%]]]]
はそれ以外では使ってはいけませんが、もし使われた場合、そのものを表します。 [CODE[[[%]]]]
以外の[[文字]]あるいは[[オクテット]]は、そのものを表します。
[FIG(railroad)[
= *
== |
=== [CODE[[[%]]]] 以外
=== =
==== [CODE[[[%]]]]
==== [[十六進数字]]
==== [[十六進数字]]
]FIG]
[HISTORY[
[67] [[パーセント符号化]]された3[[文字]]の列は、
[[RFC 2396]] では [CODE(ABNF)@en[[[escaped]]]]、
それより前は [CODE(ABNF)@en[[[escape]]]] という [[ABNF]]
[[生成規則]]で定義されていました。
]HISTORY]
[77] 何を [CODE[%]] を使って[[符号化]]し、何を元のまま残すかは、文脈によります。
個々の事情があるので、無条件にどれが正しいというものでもありません。
[EG[
[78] 例えば [CODE(URI)@en[[[https:]]]] [[URL]] の [[path segment]] 部分では、
互いの区切りの [CODE[[[/]]]] は [CODE[[[%2F]]]] としなければなりません。
また [[query]] との区切りの [CODE[[[?]]]] も、 [CODE[[[%3F]]]] としなければなりません。
[CODE[[[@]]]] は区切りとして使われませんから、 [CODE[[[@]]]]
のままでも構いませんし、 [CODE[[[%40]]]] でも構いません。
]EG]
;; >>59 を参照。
;; [174] 世間一般の解説記事の類で「[[パーセント符号化]]では○○を符号化しなければならない、
しなくてはよい、してはならない」といった記述がある場合、必ずしも正しくありませんから、
眉に唾をつけて読み飛ばしましょう。 [[RFC]] などを引用していても、
誤ったものを誤って解釈したり、十数年も前の古いものを参照していたりしますから、
下手に信用するのは危険です。
[108] [DFN[[RUBYB[パーセント符号化バイト]@en[percent-encoded byte]]]]は、
[CODE[%]] の後に[[ASCII十六進数字]]が2つ続くものです [SRC[>>43]]。
;; [109] [CODE[%]] も[[ASCII十六進数字]]も、[[符号位置]]です。
従って[[パーセント符号化バイト]]は、[[文字列]]です。
([[バイト]]と名前につくのは、それが[[バイト]]を表しているからであり、
それ自体は[[バイト列]]ではありません。)
* 大文字と小文字
[16] 現在の [CITE[URL Standard]] の[[パーセント符号化]]の定義では、
[[十六進数字]]は[[大文字]]を使うことになっています。
[61] [[パーセント復号]]では、[[大文字]]も[[小文字]]も区別せず[[復号]]されます。
[HISTORY[
[1]
[CITE[Bug in w3m-url-encode-string]] ([CODE[2007-05-11 13:00:36 +09:00]] 版) <http://emacs-w3m.namazu.org/ml/msg09323.html>
[[Webとの互換性]]のためには[[百分率符号化]]は[[大文字]]でなければならないらしい。
;; [[RFC 3986]] 的に[[大文字]]でなければならないというのは誤りで、
[[RFC 4234]] [[ABNF]] は[[大文字]]・[[小文字]]を区別しない。
([[名無しさん]] [WEAK[2007-05-11 11:19:59 +00:00]])
]HISTORY]
* 文脈
[73] [[パーセント符号化]]は、次の場面で用いられています。
[FIG(list)[ [199] [[パーセント符号化]]される場面
- [95] [[URL]]
- [74] [CODE(MIME)@en[[[application/x-www-form-urlencoded]]]] (>>90)
- [22] [[URI雛形]]の[[変数名]]
- [96] [[OAuth 1.0]] [WEAK[([[パーセント符号化]]の一種)]]
- [64] [[OAuth 2.0]] [[クライアント認証]]としての[[基本認証]]
[WEAK[([CODE(MIME)@en[[[application/x-www-form-urlencoded]]]] 版[[パーセント符号化]])]]
- [18] [[引数][引数 (HTTP)]]の拡張構文
[WEAK[([[MIME]] の定義では[[パーセント符号化]]相当のもの、[[HTTP]] の定義では[[パーセント符号化]])]]
- [19] [CODE(HTTP)@en[[[Content-Disposition:]]]] [[ヘッダー]]の
[CODE(HTTP)@en[[[filename]]]] [[引数]]
[WEAK[(実装によっては。[CODE(HTTP)@en[[[filename]]]] を参照。)]]
- [75] [[HTTP]] [CODE(HTTP)@en[[[ALPN:]]]] [[ヘッダー]]
- [[HTTP]] [CODE(HTTP)@en[Alt-Svc:]] [[ヘッダー]]
- [[HTTP]] [CODE(HTTP)@en[Slug:]] [[ヘッダー]]
- [[HTTP]] [CODE(HTTP)@en[Label:]] [[ヘッダー]]
- [17] [[変種説明]]の [CODE(HTTP)@en[[[description]]]] [[属性]]の値
- [20] [[特徴タグ]]の値 [WEAK[([[パーセント符号化]]相当)]]
- [71] [CODE(MIME)@en[[[multipart/form-data]]]]
]FIG]
* 非予約文字の百分率符号化
[2]
最後の path segment が [CODE(URI)[%2E]]
または [CODE(URI)[%2E%2E]], [CODE(URI)[%2E.]],
[CODE(URI)[.%2E]] な[[相対参照]]について。
([[名無しさん]])
[3]
[CODE(HTMLe)@en[[[a]]]] [CODE(HTMLa)@en[[[href]]]] にそのような[[相対参照]]を指定した時の[[ステータス・バー]]または[[ツールチップ]]に表示される[[絶対URI参照]]は:
- [[Firefox]] 1.5、[[Opera]] 9: [[百分率符号化]]を解いたもの
([CODE(URI)[[[.]]]] や [CODE(URI)[[[..]]]])
- [[WinIE 6]]: [[百分率符号化]]されたまま
([[名無しさん]])
[4]
その[[リンク]]を[[かちっ]]たときに飛ばされる[[文書]]の [[URI]]
([[アドレス・バー]]の表示や [CODE(JS)@en[[[location]].[[href]]]]) は:
- [[Firefox]] 1.5、[[WinIE 6]]: [CODE(URI)[[VAR@ja[[[基底URI]]]]/%2E]] など、
[[百分率符号化]]のまま
- [[Opera]] 9: [CODE(URI)[[VAR@ja[[[基底URI]]]]/.]] など、
[[百分率符号化]]を解いた値
([[名無しさん]])
[5]
>>2 のような path segment が含まれるものの、最後の path
segment ではない[[相対参照]] (例えば [CODE(URI)[%2E/]])
([[名無しさん]])
[6]
>>5 [[ステータス・バー]]などの表示:
- [[Firefox]] 1.5、[[Opera]] 9: [CODE(URI)[[[.]]]] や
[CODE(URI)[[[..]]]] として解釈したときの[[絶対URI参照]]
- [[WinIE]] 6: [[百分率符号化]]されたまま、
特別な意味を持たないと解釈した時の[[絶対URI参照]]
([[名無しさん]])
[7]
>>5 飛ばされる [[URI]]:
- [[Firefox]] 1.5、[[Opera]] 9: 特別な意味を持つと解釈した時の[[絶対URI参照]]
- [[WinIE 6]]: [[百分率符号化]]されたまま、特別な意味を持たないと解釈した時の[[絶対URI参照]]
([[名無しさん]])
[8]
[[URI scheme]] [Q@en[[CODE(URI)@en[http]]]] の一部または全部が[[百分率符号化]]されている[[URI参照]]
([[名無しさん]])
[9]
>>8 表示、移動先とも、 [[Firefox]] 1.5、[[Opera]] 9、
[[WinIE 6]] のいずれも[[相対参照]]と解釈
([[名無しさん]])
[10]
[[ASCII]] [CODE(URI)@en[[[hostname]]]] の一部または全部が[[百分率符号化]]されている[[URI参照]]
([[名無しさん]])
[11]
>>10:
表示: [[Firefox]] 1.5、[[Opera]] 9、[[WinIE 6]] とも、
[[百分率符号化]]を解いたもの
([[名無しさん]])
[12]
>>10 移動先:
- [[Opera]] 9、[[WinIE 6]]: [[百分率符号化]]を解いた[[ホスト]]
- [[Firefox]] 1.5:
-- [CODE(URI)@en[[[.]]]] が[[百分率符号化]]されているなら、[[百分率符号化]]をすべて解いた[[ホスト]]
-- そうでないなら、[[百分率符号化]]されたままの[[ホスト]]
(見つからないというエラーになる)
([[名無しさん]])
[13]
[[ポート番号]]の一部または全部が[[百分率符号化]]されている場合:
表示、移動先とも:
- [[Opera]] 9、[[WinIE 6]]: [[百分率符号化]]を解いた[[ポート番号]]
- [[Firefox]] 1.5: [[百分率符号化]]を解かない状態で、最初の[[数字]]の連続
-- 例えば、 [CODE(URI)[%38%30]] なら [CODE[[[30]]]] と解釈する
([[名無しさん]])
[14]
テスト: <http://suika.fam.cx/~wakaba/-temp/test/uri/percent/unreserved/>
([[名無しさん]] [WEAK[2007-05-21 05:49:59 +00:00]])
[15]
[CITE@ja-JP[akr流(2007-02-22)]] ([[akr]] 著, [TIME[2007-02-26 17:00:01 +09:00]] 版) <https://www.codeblog.org/blog/akr/20070222.html>
* [CODE(URI)[%]]
[98] [CODE[[[%]]]] は、[[オクテット]]を表す以外では使うことができません。
これは[[パーセント符号化]]のみならず、 [[URL]] 全体での原則です。
[99] しかし現実には [CODE[[[%]]]] の後に[[十六進数字]]2桁が続かない場合もあります。
そのような場合には、 [CODE[[[%]]]] そのものを表すと解釈することになっています。
[100] ただし、[[鯖]]などの実装によっては[[致命的エラー]]とみなすこともあります。
[101] [[URL]] における [CODE[[[%]]]] には次のような特別な使い方もあり、
[[パーセント符号化]]と混在するかもしれません。
[FIG(short list)[
- [[[CODE[%u]]符号化]] (>>36)
- [[proto-URL]] の [CODE[[[%s]]]]
- [[フォーム提出]]における [CODE[[[%%]]]] や [CODE[[[%%%%]]]]
]FIG]
[62] 長い [[URL]] が途中で分断されるなどの理由で [CODE[[[%]]]] で終わる [[URL]]
が生成されてしまうことがあります。
[63] [[メッセージID]]でよく使われる文字である [CODE[[[%]]]]
を無変換で [[URL]] の一部に含めてしまい[[メーリングリスト]]の[[アーカイブ]]などで単独の
[CODE[[[%]]]] が混入することがあります。
* [CODE(URI)@en[+]]
[89] [CODE(MIME)@en[[[application/x-www-form-urlencoded]]]] の仕様より、
[[パーセント符号化]]された文字列において [CODE[[[+]]]]
が [CODE[[[0x20]]]] ないしは [CODE(char)[[[U+0020]]]] と解釈されることが多々あります
([[プラスパーセント符号化]])。
これは [CODE(MIME)@en[[[application/x-www-form-urlencoded]]]] 側の仕様であって、
[[パーセント符号化]]の一部ではありません。
[158] [[URL]] のある部分がただの[[パーセント符号化]]であるか[[プラスパーセント符号化]]であるかは、
その部分に関する規定に依存しており、どちらであるかを判断するのは難しい問題ですが、
おおむね次のように決定できます。
[FIG(steps)[
= [159] [[URL]] の一部なら、
== [162] [[query][URL query]] なら、
=== [165] [[URL scheme]] が
[CODE(URI)@en[http:]]/[CODE(URI)@en[https:]]/[CODE(URI)@en[ws:]]/[CODE(URI)@en[wss:]] なら、
==== [166] [CODE(MIME)@en[application/x-www-form-urlencoded]] の可能性が高い
=== [167] それ以外なら、
==== [168] おそらくただの[[パーセント符号化]]
== [163] それ以外なら、
=== [164] おそらくただの[[パーセント符号化]]
= [160] [CODE[POST]] データなら、
== [161] [CODE(MIME)@en[application/x-www-form-urlencoded]]
]FIG]
[EG[
[21] [CODE(URI)@en[mailto:]] [[URL]] の [[query][URL query]] や[[媒体素片]]は
[CODE(MIME)@en[[[application/x-www-form-urlencoded]]]]
と似ていますが、 [CODE(URI)[[[+]]]] に特別な解釈を与えていません。
]EG]
[EG[
[169] [CODE(URI)@en[http:]]/[CODE(URI)@en[https:]] [[URL]] の [[query][URL query]]
の解釈は、完全に[[サーバー]]側に委ねられています。
[[サーバー]]の管理者や[[Webアプリケーション]]の[[開発者]]は、
ただの[[パーセント符号化]]でも、[CODE(MIME)@en[application/x-www-form-urlencoded]]
でも、他の独自の形式でも、個々の事情に合わせて任意の形式を採用できます。
[[クライアント]]は事前情報無しにどちらを利用するべきか決定できません。
;; もっとも、[[フォームの提出]]は [CODE(MIME)@en[application/x-www-form-urlencoded]]
に固定されていますから、[[Webアプリケーション]]の開発者はそれに合わせた方が楽で無難です。
]EG]
[104] ただの[[パーセント符号化]]で [CODE[[[+]]]]
が [CODE[[[0x20]]]] ないしは [CODE(char)[[[U+0020]]]] と解釈する/させることは誤りです。
ただの[[パーセント符号化]]における [CODE[[[+]]]] は、
[CODE[[[+]]]] そのものを表します。
(文脈によっては、区切りなど特別な意味が与えられていることもあります。)
[EG[
[79] [[JavaScript]] の [CODE(JS)@en[[[encodeURIComponent]]]] は[[パーセント符号化]]のためのもので、
[CODE(MIME)@en[[[application/x-www-form-urlencoded]]]] のためのものではありませんから、
[CODE(char)[[[U+0020]]]] [CODE(charname)@en[[[SPACE]]]]
は [CODE[[[+]]]] ではなく、 [CODE[[[%20]]]] と[[符号化]]されます。
]EG]
;; [103] [CODE(MIME)@en[[[application/x-www-form-urlencoded]]]] でも [CODE[[[%20]]]]
が禁止されているわけではありませんから、常に [CODE[[[%20]]]] を使うこととし
[CODE[[[+]]]] を使わないとしても、問題はありません。
[173] 世間の[[パーセント符号化]]や [[URL]] や[[フォーム]]の実装や解説の類では、
[CODE(MIME)@en[application/x-www-form-urlencoded]] を含めいろいろな[[パーセント符号化]]のバリエーションについて、
混同して混乱していることがよくあります。[[仕様書]]などを引用していても、
誤った解釈から誤った結論を導いていることが少なくありませんから、
不用意に信用してはいけません。
* 符号化文字集合
[85] [[パーセント符号化]]を[[復号]]したものは、[[バイト列]] ([[オクテット列]]) です。
[86] これをどう解釈するかは、[[パーセント符号化]]が用いられる文脈によって異なります。
[87] 近代的な仕様やアプリケーションでは、[[UTF-8]] によって[[符号化]]された[[文字列]]として解釈します。
[110] [[パーセント符号化バイト]]の列は、
[[パーセント復号]]してから [[UTF-8 decode without BOM or fail]]
を適用しても[[失敗]]とならない[SHOULD[べきです]] [SRC[>>43]]。
[88] 歴史的には [[UTF-8]] 以外によって解釈されることも多々ありました。
[[文字列]]ではない[[バイト列]]として解釈されることもあります。
[23] [[URL]] 一般においては[[パーセント符号化]]された[[バイト列]]の[[文字コード]]が何であるか明記する仕組みはありません。
関連する仕様書によって明示的に規定されていることもあれば、
慣習によることや、受信者の[[自動判別]]に依存していることもあります。
[24] [CODE(MIME)@en[[[application/x-www-form-urlencoded]]]] では、
[[_charset_ hack]] や同様の手法で[[パーセント符号化]]された[[バイト列]]の[[文字コード]]が記述されることもあります。
;; [CODE(MIME)@en[[[application/x-www-form-urlencoded]]]] 参照。
[25] >>102 が提案する [[HTTPヘッダー]]のように外部に[[文字コード]]を明記する手法が提案されたり、
部分的に実装されたりすることもありましたが、広く普及しているものはありません。
* [CODE(URI)[%u]] 符号化
[36] [[ECMAScript]] によって導入された [CODE(URI)[[[%u]]]] [[符号化]]は、
[[URL]] での利用が認められたことはありませんが、現在でも稀に用いられます。
詳しくは [CODE(URI)[[[%u]]]] の項をご覧ください。
* [CODE(MIME)@en[application/x-www-form-urlencoded]]
[90] [CODE(MIME)@en[[[application/x-www-form-urlencoded]]]] は[[パーセント符号化]]から派生した
[[MIME型]]です。[[フォームの提出]]で採用されています。
;; [91] 詳しくは [CODE(MIME)@en[[[application/x-www-form-urlencoded]]]] の項を参照してください。
[92] [CODE(MIME)@en[[[application/x-www-form-urlencoded]]]] は単独の[[実体]]として使われる他に、
[[URL]] の [[query]] として使われることがあります。 >>89 の通り、 [CODE(URI)[[[+]]]]
の解釈がただの[[パーセント符号化]]とは異なります。 (この [[MIME型]]に限らず)
[CODE(URI)@en[[[http:]]]] [[URL]] の [[query]] の解釈はサーバーに委ねられていますから、
当該サーバーや当該サーバーと事前に知識を共有した者以外は [[query]] をどう解釈するべきか確実には決定できません。
* パーセント符号化演算
[59] [[パーセント符号化]] (や[[パーセント復号]]) の方法は、
入力が[[文字列]]、[[バイト列]]、あるいは既に[[パーセント符号化]]を含む [[URL]] の一部分のいずれであるかにより、
またそれを [[URL]] のどの部分で用いるかにより、色々なバリエーションが存在しています。
;; [60] 多くの場合は特に区別せずに「[[パーセント符号化]]」や「[[URI符号化]]」
などと曖昧に呼ばれているので、しばしば混同され、[[相互運用性]]の問題を生じさせる原因ともなっています。
** 基本的な演算
[111] [[バイト]][VAR[バイト]]の[DFN[[RUBYB[パーセント符号化]@en[percent encode]]]]は、
次のようにしなければ[MUST[なりません]] [SRC[>>43]]。
[FIG(steps)[
= [112] [CODE[%]] の後に、2桁の[[大文字]]の[[十六進数]]で[VAR[バイト]]を表現したものを連結した[[文字列]]を返します。
]FIG]
;; [129] [[仕様書]]上の[[パーセント符号化]]は、[[バイト列]]ではなく[[バイト]]に関する操作として定義されています。
[113] [[バイト列]][VAR[入力]]の[DFN[[RUBYB[パーセント復号]@en[percent decode]]]]は、
次のようにしなければ[MUST[なりません]] [SRC[>>43]]。
[FIG(steps)[
= [114] [VAR[出力]]を、[[空]]の[[バイト列]]に設定します。
= [115] [VAR[入力]]の各[[バイト]][VAR[バイト]]について順に、
== [119] [VAR[バイト]]が処理済みでなければ、
=== [116] [VAR[バイト]]が 0x25 なら、
==== [120] [VAR[バイト]]の次に2[[バイト]]あって、それがいずれも
[0x30, 0x39], [0x41, 0x46], [0x61, 0x66] のいずれかの[[範囲]]に含まれるなら、
===== [121] [VAR[復号バイト]]を、当該2[[バイト]]を [[UTF-8 decode without BOM]]
した結果を[[十六進数]]として解釈した結果に設定します。
===== [122] [VAR[復号バイト]]を[VAR[出力]]の末尾に追加します。
===== [123] 当該2[[バイト]]を処理済みとします。
==== [124] それ以外なら、
===== [125] [VAR[バイト]]を[VAR[出力]]の末尾に追加します。
=== [117] それ以外なら、
==== [118] [VAR[バイト]]を[VAR[出力]]の末尾に追加します。
= [126] [VAR[出力]]を返します。
]FIG]
;; [127] つまり、 [CODE[%]] と [[ASCII十六進数字]]2桁の列はその[[十六進数]]の表す[[バイト]]に置換され、
それ以外の[[バイト]] (不適切な [CODE[%]] も含みます。) はそのまま出力されます。
[128] [[パーセント復号]]の結果は[[バイト列]]で、それをどう解釈するかは呼び出し元に委ねられています。
ただし、[VAR[入力]]に[[ASCIIバイト]]以外が含まれる場合、
[[UTF-8 decode without BOM]] 以外の方法で扱うのは安全ではないかもしれないので、
避ける[SHOULD[べき]]だ [SRC[>>43]] とされています。
-*-*-
[130] [VAR[[[符号位置]]]]の[VAR[符号化集合]]に関する[DFN[[RUBYB[UTF-8パーセント符号化]@en[UTF-8 percent encode]]]]は、
次のようにします [SRC[>>43]]。
[FIG(steps)[
= [131] [VAR[符号位置]]が[VAR[符号化集合]]に含まれ''ない''なら、
== [132] [VAR[符号位置]]を返します。
= [133] それ以外なら、
== [134] [VAR[バイト群]]を、[VAR[符号位置]]を[[UTF-8符号化]]した結果に設定します。
== [135] [VAR[バイト群]]の各[[バイト]][VAR[バイト]]を、
[VAR[バイト]]を[[パーセント符号化]]した結果に置換します。
== [136] [VAR[バイト群]]を返します。
]FIG]
;; [137] [VAR[符号化集合]]は、[[符号位置]]の[[集合]]です。
[[URL Standard]] では[[C0制御パーセント符号化集合]]、[[パスパーセント符号化集合]]、[[素片パーセント符号化集合]]、
[[userinfoパーセント符号化集合]]が定義されています。
[143] [[UTF-8パーセント符号化]]は、
[[フォームの提出]]を除けば、[[URL構文解析器]]でのみ使われているようです。
-*-*-
[193] [DFN[[RUBYB[文字列パーセント復号]@en[string percent decode]]]]は、
[[文字列]][VAR[入力]]を次のようにすることをいいます。 [SRC[>>43]]
[FIG(steps)[
= [194] [VAR[バイト群]]を、 [VAR[入力]]を [[UTF-8符号化]]した結果に設定します。
= [195] [VAR[バイト群]]を[[パーセント復号]]した結果を返します。
]FIG]
[201] [[入力]]は[[文字列]]ですが、[[出力]]は[[バイト列]]となります。
[FIG(short list)[ [196] [[文字列パーセント復号]]する場面
- [[ホスト構文解析器]]
- [[[CODE(URI)@en[data:]] URL処理器]]
- [CODE[javascript:]]
- [[文書の示された場所]]
]FIG]
** [CODE(MIME)@en[application/x-www-form-urlencoded]]
[55] [CODE(MIME)@en[[[application/x-www-form-urlencoded]]]]
は、任意の[[文字符号化]]によって[[符号化]]してから[[パーセント符号化]]する方法を規定しています。
ここでは 0x20 は [CODE[[[+]]]] で表す点が通常の[[パーセント符号化]]と異なります。
[56] これに対応する[[復号]]の方法も規定されています。
;; 詳しくは [CODE(MIME)@en[[[application/x-www-form-urlencoded]]]] を参照。
[140] [CODE(URI)@en[mailto:]] [[URL]] への[[フォームの提出]]では、
[CODE(MIME)@en[application/x-www-form-urlencoded]] として[[符号化]]した後で
[CODE[+]] を [CODE[%20]] に置換する場合があります。つまり標準的な[[パーセント符号化]]となります。
** OAuth 1.0 パーセント符号化
[29] [[OAuth 1.0]] ([[RFC 5849]]) は、次のような[[パーセント符号化]]の手順を規定しています
[SRC[>>26]]。
[FIG(steps)[
= [52] [[テキスト]]の値なら、 [[RFC 3629]] [[UTF-8]] で[[符号化]]します。
[[バイナリ]]の値には適用しません。
= [53] [[RFC 3986]] [[非予約文字]]''以外''の[[オクテット]]を [CODE[[[%]]]]
と2桁の[[大文字]]の[[十六進数字]]の列に置き換えます。
]FIG]
[51] この[[符号化]]方式は [[OAuth 1.0]] における [CODE(HTTP)@en[[[Authorization:]]]]
[[ヘッダー]]と、[[署名基底文字列]]で用いられます。
[CODE(MIME)@en[[[application/x-www-form-urlencoded]]]] ではこの方式は使いません。 [SRC[>>26]]
[54] [[復号]]の方法はなぜか明記されていません。 [CODE(HTTP)@en[[[Authorization:]]]]
では[[復号]]も必要なはずですが・・・。
[156] [[テキスト]]の値の [[OAuth 1.0パーセント符号化]]は、
[[認証された要求]]の作成で呼び出されます。
[155] [[バイナリー]]の値を[[OAuth 1.0パーセント符号化]]することは無さそうに思いますが、
既に ([[OAuth 1.0]] の[[署名]]以外) 作り終えた[[要求]]の一部を取り出して[[署名基底文字列]]を作成する時、
[[バイナリー]]入力の[[パーセント符号化]]が必要となります。
[185] [[AWS]] の[[ドキュメント]]で用いられる[[関数]] [DFN[[CODE[UriEncode()]]]]
[SRC[>>184]] は、[[''非''予約文字]]''以外''を[[パーセント符号化]]することを求めています。
これは [[OAuth 1.0]] 方式の[[パーセント符号化]]に[[バイナリー]]入力を与えた場合と同じです。
[REFS[
- [184] [CITE[Signature Calculations for the Authorization Header: Transferring Payload in a Single Chunk (AWS Signature Version 4) - Amazon Simple Storage Service]] ([TIME[2017-02-11 04:52:51 +09:00]]) <http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html>
]REFS]
** JavaScript [CODE(JS)@en[encodeURI]] / [CODE(JS)@en[decodeURI]]
[50] [[JavaScript]] の [DFN[[CODE(JS)@en[encodeURI()]]]] [SRC[>>144]] は、
[[UTF-8]] で[[パーセント符号化]]するものです。
[[サロゲートペア]]でない[[サロゲート]]が含まれると、失敗します。
[148] [DFN[[CODE(JS)@en[decodeURI()]]]] [SRC[>>144]] は、それに対応する[[復号]]の方法です。
[[予約文字]]は、[[符号化]]されたまま残します。
[[パーセント符号化]]として、または [[UTF-8]] として不適切な入力だと、失敗します。
[175] これらの[[関数]]が有用な場面は意外とありません。ほとんどの場合は
[CODE(URI)@en[encodeURIComponent]]/[CODE(URI)@en[decodeURIComponent]]
の方が適切です。
[HISTORY[
[147] [CITE@ja[M.C.P.C.: MacIE5でdecodeURIを実装]] ([TIME[2009-02-03 21:32:43 +09:00]] 版) <http://blog.dtpwiki.jp/dtp/2007/11/macie5decodeuri_d127.html>
]HISTORY]
** JavaScript [CODE(JS)@en[encodeURIComponent]] / [CODE(JS)@en[decodeURIComponent]]
[150] [[JavaScript]] の [DFN[[CODE(JS)@en[encodeURIComponent()]]]] [SRC[>>144]] は、
[[UTF-8]] で[[パーセント符号化]]するものです。
[[サロゲートペア]]でない[[サロゲート]]が含まれると、失敗します。
;; [187] [[UTF-8パーセント符号化]]を特定の[VAR[符号化集合]]で実行したものと解釈できますが、
[[UTF-8パーセント符号化]]の入力は[[文字列]]であるのに対し、
[[JavaScript]] の入力は[[16ビット符号単位]]列なので[[サロゲート]]の扱いが規定されている点が異なります。
[153] [DFN[[CODE(JS)@en[decodeURIComponent()]]]] [SRC[>>144]] は、それに対応する[[復号]]の方法です。
[[パーセント符号化]]として、または [[UTF-8]] として不適切な入力だと、失敗します。
[176] [[JavaScript]] で[[文字列]]処理により [[URL]] を組み立てる場合や、
[[URL]] の一部から符号化された[[文字列]]を取り出す場合には、
これらの[[関数]]が適切な場合が多いです。
[177] [CODE(MIME)@en[application/x-www-form-urlencoded]] でない普通の[[パーセント符号化]]ですから、
[CODE[+]] の特別な処理は行われません。 [[URL query]] の処理で特に
[CODE(MIME)@en[application/x-www-form-urlencoded]] を特に扱う必要がある時は、
処理前または処理後に適宜 [CODE[+]] を置換しなければなりません。
[HISTORY[
[149] [CITE[encodeURIComponent メソッド]] <http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/script56/html/js56jsmthEncodeURIComponent.asp>
[151] [[WinIE 6]], [[Opera]] 9, [[Firefox]] 2 のいずれも、
[CODE(URI)@en[encodeURIComponent]] に[[サロゲート・ペア]]の片割れが与えられると正しく
[CODE(JS)@en[[[URIError]]]] を投げるようです。
[REFS[
- [152] <http://suika.fam.cx/~wakaba/-temp/test/js/uri/decode-uri-component/duc-1.html>
]REFS]
[145] [[Firefox]] 2 では、 [CODE(URI)@en[decodeURIComponent]] に
[CODE(char)[[[U+FFFE]]]] と [CODE(char)[[[U+FFFF]]]]
(に相当する [CODE(URI)[%EF%BF%BE]] と [CODE(URI)[%EF%BF%BF]])
を与えると、 [CODE(char)[[[U+FFFD]]]] を返します。
[[WinIE 6]] や [[Opera]] 9 ではそのようなことはありません。
[146] [[Opera]] 9 は [CODE(URI)@en[%00]] を[[復号]]しないようです。
[[Firefox]] 2 や [[WinIE 6]] ではそのようなことはありません。
]HISTORY]
** JavaScript [CODE(JS)@en[escape()]] / [CODE(JS)@en[unescape()]]
[48] [[JavaScript]] の [CODE(JS)@en[escape()]] は、 [[ASCII文字]]を[[パーセント符号化]]し、
[[非ASCII文字]]は [CODE[%u]] 符号化するものです。
[49] [CODE(JS)@en[unescape()]] はそれを[[復号]]するものです。
;; [CODE[%u]] 参照。
[178] これらの[[関数]]は歴史的なもので、必要となる場面はほとんどありません。
** ALPN プロトコル名のパーセント符号化
[138] [[ALPNプロトコル名]]を[[パーセント符号化]]する場合、
[[HTTP]] の[[字句]]で表せる[[オクテット]] ([CODE[%]] 以外) はそのまま、
それ以外は[[パーセント符号化]]し、[[パーセント符号化]]には[[大文字]]を使うことが求められています。
;; [CODE(HTTP)@en[ALPN:]]、[CODE(HTTP)@en[Alt-Svc:]] を参照。
[142] この方法は、[[復号]]しないで[[符号化]]された状態で比較に使うことが想定されています。
** CGI における取り扱い
[40] [[文字列]]を[[パーセント符号化]]する場合、 [CODE(ABNF)@en[[[reserved]]]] に含まれていない[[文字]]を[[符号化]]しては[['''なりません''']]
[SRC[>>39]]。
[REFS[
- [39] [CITE@en[RFC 3875 - The Common Gateway Interface (CGI) Version 1.1]] ([TIME[2011-11-20 06:09:05 +09:00]] 版) <http://tools.ietf.org/html/rfc3875#section-2.3>
]REFS]
** URI Template のパーセント符号化
[57] [[URI Template]] は、[[雛形リテラル]]の[[パーセント符号化]]と[[文字列]]値の[[パーセント符号化]] ([[雛形式]]参照。)
の2種類の[[パーセント符号化]]の方法を規定しています。
** IRI および IRI もどきのパーセント符号化
[58] [[IRI]] および各種の [[IRI]] もどきは、[[パーセント符号化]]により [[URI]]
を得る方法を規定しています。細部が異なる色々なバリエーションがあります。
;; [[IRI]] 参照。
** パーセント符号化される文字/バイトの比較
[141]
[FIG(table)[
:name: 符号化の方式
:encoded: 符号化されるもの
:name: [[UTF-8パーセント符号化]] + [[C0制御パーセント符号化集合]]
:encoded: <https://chars.suikawiki.org/set/%24url%3Asimple-encode-set>
:name: [[UTF-8パーセント符号化]] + [[パスパーセント符号化集合]]
:encoded: <https://chars.suikawiki.org/set/%24url%3Adefault-encode-set>
:name: [[UTF-8パーセント符号化]] + [[素片パーセント符号化集合]]
:encoded: <https://chars.suikawiki.org/set/%24url%3Afragment-encode-set>
:name: [[UTF-8パーセント符号化]] + [[userinfoパーセント符号化集合]]
:encoded: <https://chars.suikawiki.org/set/%24url%3Auserinfo-encode-set>
:name: [CODE(MIME)@en[application/x-www-form-urlencoded]]
:encoded: <https://chars.suikawiki.org/set?expr=-%24url%3Aform%3Aunencoded-byte-char>
:name: [[ALPN]]
:encoded: <https://chars.suikawiki.org/set?expr=-%24rfc7230%3Atchar|[%25]>
:name: [[OAuth 1.0]]
:encoded: <https://chars.suikawiki.org/set?expr=-%24rfc3986%3Aunreserved>
:name: [CODE(JS)@en[escape()]]
:encoded: <https://chars.suikawiki.org/set?expr=-%24ecma262%3Aescape-not-escaped>
:name: [CODE(JS)@en[encodeURI()]]
:encoded: <https://chars.suikawiki.org/set?expr=-%24ecma262%3AunescapedURISet>
:name: [CODE(JS)@en[encodeURIComponent()]]
:encoded: <https://chars.suikawiki.org/set?expr=-%24ecma262%3AunescapedURIComponentSet>
]FIG]
[154] [[パーセント符号化]]される[[文字]]の比較:
[REFS[
- [CITE@en[Compare character sets]] ([TIME[2016-06-07 17:13:21 +09:00]]) <https://chars.suikawiki.org/set/compare?expr=$url:simple-encode-set&expr=$url:default-encode-set&expr=$url:userinfo-encode-set&expr=-$url:form:unencoded-byte-char&expr=-$rfc7230:tchar|[%]&expr=-$rfc3986:unreserved&expr=-$ecma262:escape-not-escaped&expr=-%24ecma262%3AunescapedURISet&expr=-%24ecma262%3AunescapedURIComponentSet>
]REFS]
** 各実装の符号化演算
[171] 歴代の [[URL]] の[[仕様書]]の[[パーセント符号化]]の規定があまり明確でなかったことや、
[[パーセント符号化]]が文脈によって色々な使われ方をしていることもあり、
各種[[プログラミング言語]]や[[ライブラリー]]、[[フレームワーク]]等の[[パーセント符号化]]・[[パーセント復号]]の演算も多種多様で混乱しており、
利用する時は正しい物を注意深く選択しなければなりません。
[172] 例えば [[PHP]] の [CODE[http_build_query]] [SRC[>>170]] は、
符号化の方式を指定するオプションを用意していますが、
[CODE[PHP_QUERY_RFC1738]] は「[[RFC 1738]] + [CODE(MIME)@en[application/x-www-form-urlencoded]]」、
[CODE[PHP_QUERY_RFC3986]] は「[[RFC 3986]]」の方式と説明されています。
[[RFC 3986]] は古い [[URL]] の仕様書で、 [[RFC 1738]] は更に何代か古い [[URL]]
の[[仕様書]]です。
前者が実はただの[[パーセント符号化]]ではなく
[CODE(MIME)@en[application/x-www-form-urlencoded]] 方式だという重要な情報は定数名に現れず、
ドキュメントを読まないとわかりません。
また [[RFC 1738]] と [[RFC 3986]] とで [CODE(ABNF)@en[reserved]]
の定義が異なるためにどの文字を[[パーセント符号化]]するかが両方式では違っているということのようです。
しかし、これではいつどちらを使うべきなのかがさっぱりわかりません
([[PHP]] のドキュメントにも、もちろん両 [[RFC]] にも書かれていません)。
[186] [[プログラミング言語]]によっては、入出力が[[文字列]]と[[バイト列]]のいずれであるかにも、
注意が必要です。
[188] 近代的な[[ライブラリー]]では、 [[UTF-8パーセント符号化]]と、
その逆演算 ([[パーセント復号]]してから[[UTF-8復号]]) の[[関数]]が用意されていることがあります。
;; [[JavaScript]] の [CODE[encodeURIComponent]]/[CODE[decodeURIComponent]]
に相当するものですが、やはり細部が異なることがあります。
* 歴史
** RFC 2396 (URI) 2.4. Escape Sequences
> Data must be escaped if it does not have a representation using an
unreserved character; this includes data that does not correspond to
a printable character of the US-ASCII coded character set, or that
corresponds to any US-ASCII character that is disallowed, as explained below.
データは、[[非予約]]文字を使って表現することができない場合には、
escape しなければなりません。これには、
US‐ASCII 符号化文字集合の[[印字可能文字]]に対応しないデータや対応する
US‐ASCII 文字が禁止されているデータを含みます。
*** 2.4.1. Escaped Encoding
> An escaped octet is encoded as a character triplet, consisting of the
percent character "%" followed by the two hexadecimal digits
representing the octet code. For example, "%20" is the escaped
encoding for the US-ASCII space character.
Escape するオクテットは、百分率記号
[CODE(char)[%]] とそれに続くオクテット符号を表現する2つの16進数字で構成される3文字列として符号化されます。
例えば、 [CODE(URI)[%29]] は US‐ASCII
間隔文字の escape した符号化です。
>
- [CODE(ABNF)[[DFN[escaped]] = "%" hex hex]]
- [CODE(ABNF)[[DFN[hex]] = digit | "A" | "B" | "C" | "D" | "E" | "F" | "a" | "b" | "c" | "d" | "e" | "f"]]
*** 2.4.2. When to Escape and Unescape
> A URI is always in an "escaped" form, since escaping or unescaping a completed URI might change its semantics. Normally, the only time escape encodings can safely be made is when the URI is being created from its component parts; each component may have its own set of characters that are reserved, so only the mechanism responsible for generating or interpreting that component can determine whether or not escaping a character will change its semantics. Likewise, a URI must be separated into its components before the escaped characters within those components can be safely decoded.
URI は、常に「escape された」形です。
完全な URI を escape したり unescape したりすると、
その意味が変わってしまうかもしれません。
通常、 escape 符号化を安全に行うことができる唯一の機会は、
URI をその部品部分から作成するときです。
各部品は自身の予約されている文字の集合を持っているかもしれませんから、その部品を生成したり解釈したりするのに責を有する機構だけが文字を escape することでその意味が変わるかどうかを決定できます。
同様に、部品中の escape された文字を安全に復号する前には URI を部品群に分離しなければなりません。
> In some cases, data that could be represented by an unreserved
character may appear escaped; for example, some of the unreserved
"mark" characters are automatically escaped by some systems. If the
given URI scheme defines a canonicalization algorithm, then
unreserved characters may be unescaped according to that algorithm.
For example, "%7e" is sometimes used instead of "~" in an http URL
path, but the two are equivalent for an http URL.
場合によっては、非予約文字で表現できるデータが escape
されて現れることがあります。例えば、幾つかの非予約「マーク」文字を自動的に escape するシステムがあります。
その URI scheme が正規化算法を定義しているのなら、
非予約文字はその算法に従って escape を解いて構いません。
例えば、 [CODE(URI)[%7e]] は [CODE(URI)[[[http]]]] URL
経路で時々 [CODE(URI)[~]] の代わりに使われますが、
[CODE(URI)[http]] URL では2つは同等です。
> Because the percent "%" character always has the reserved purpose of being the escape indicator, it must be escaped as "%25" in order to be used as data within a URI. Implementers should be careful not to
escape or unescape the same string more than once, since unescaping
an already unescaped string might lead to misinterpreting a percent
data character as another escaped character, or vice versa in the
case of escaping an already escaped string.
百分率 [CODE(URI)[%]] 文字は escape 指示子として使う目的で常に予約されているので、 URL 中でデータとして使うためには [CODE(URI)[%25]] と escape しなければなりません。
実装者は、同じ文字列を複数回 escape したり解いたりしないように注意を払うべきです。
既に escape を解いた文字列を更に escape 解除すると百分率データ文字を他の escape された文字列を誤解したり、逆に既に escape された文字列を escape してしまったりします。
** RFC 1738 (URL) 2.2 の一部
>
:No corresponding graphic US-ASCII:
URLs are written only with the graphic printable characters of the
US-ASCII coded character set. The octets 80-FF hexadecimal are not
used in US-ASCII, and the octets 00-1F and 7F hexadecimal represent
control characters; these must be encoded.
:対応する図形が US‐ASCII にない:URL は US‐ASCII
符号化文字集合の図形印字可能文字のみによって書きます。
オクテット [CODE[80]]‐[CODE[FF]] (16進)
は US‐ASCII では使っておらず、
オクテット [CODE[00]]‐[CODE[7F]] (16進)
は[[制御文字]]を表現します。
これらは符号化しなければなりません。
** RFC 2396 (URI) 2.4.3. Excluded US-ASCII Characters
> Although they are disallowed within the URI syntax, we include here a
description of those US-ASCII characters that have been excluded and
the reasons for their exclusion.
これらの US‐ASCII 文字は URI 構文では禁止されていますが、
これらの US‐ASCII 文字が除外されていることとその除外の理由を説明するためにここに含めます。
> The control characters in the US-ASCII coded character set are not
used within a URI, both because they are non-printable and because
they are likely to be misinterpreted by some control mechanisms.
US‐ASCII 符号化文字集合の制御文字は URI
中では使いません。これらは非印字可能ですし。
誤解する制御機構もあるでしょうからです。
>
- [CODE(ABNF)[[DFN[control]] = <US-ASCII coded characters 00-1F and 7F hexadecimal>]]
> The space character is excluded because significant spaces may
disappear and insignificant spaces may be introduced when URI are
transcribed or typeset or subjected to the treatment of word-processing programs. Whitespace is also used to delimit URI in many contexts.
間隔文字は除外されていますが、これは、 URI
が転記されたり植字されたり文書処理プログラムの処理対象になったりしたときに意味のある間隔が消滅したり、意味のない間隔が URI に加わったりするかもしれないからです。
空白は多くの場面で URI を区切るためにも使われます。
>
- [CODE(ABNF)[[DFN[space]] = <US-ASCII coded character 20 hexadecimal>]]
> The angle-bracket "<" and ">" and double-quote (") characters are
excluded because they are often used as the delimiters around URI in
text documents and protocol fields. The character "#" is excluded
because it is used to delimit a URI from a fragment identifier in URI
references (Section 4). The percent character "%" is excluded because
it is used for the encoding of escaped characters.
角括弧 [CODE(char)[<]] 及び [CODE(char)[>]]
並びに二重引用符 [CODE(char)["]] は、
文章やプロトコル欄の中においてしばしば URI
を囲む区切子として使われるので除外します。
文字 [CODE(char)[#]] は URI と[[素片識別子]]を [[URI参照]]中で区切るのに使うので除外します。
百分率文字 [CODE(char)[%]] は escape 文字の符号化に使うので除外します。
>
- [CODE(ABNF)[[DFN[[[delims]]]] = "<" | ">" | "#" | "%" | <">]]
> Other characters are excluded because gateways and other transport
agents are known to sometimes modify such characters, or they are
used as delimiters.
他の文字は、関門や他の転送エージェントが時々これらの文字を修正したり区切子に使ったりすることが知られているので除外します。
>
- [CODE(ABNF)[[DFN[[[unwise]] = "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`"]]]]
> Data corresponding to excluded characters must be escaped in order to
be properly represented within a URI.
除外文字に対応するデータは、 URI 中で適切に表現するためには
escape しなければなりません。
* メモ
[81] [[BNF]] では [CODE(ABNF)[[[escape]]]] とか [CODE(ABNF)[[[escaped]]]] とかと表しています。
[82] なんにせよ、 URI 符号化は[[シフトJIS]] とか [[UTF-8]] のような[[文字符号化]]とは層が異なることには注意しておいて損はないです。
[83] まあ、 URI 符号化も「文字」符号化には違いないけど... 言葉遊びは勘弁して。
[84] URI エスケープ符号化は [[SGML]]/[[XML]] の[[文字参照]]と同じ層に位置すると考えればいいかなぁ。[WEAK[(細かいことをいうと SGML はあくまで文書文字集合中の文字を参照するのに対し、 URI エスケープ符号はオクテットを符号化したものに過ぎないのは重大な差異だけど、この際どうでもいいか。)]]
[31] [CITE[IRC logs: freenode / #whatwg / 20091229]]
([TIME[2010-01-06 07:47:25 +09:00]] 版)
<http://krijnhoetmer.nl/irc-logs/whatwg/20091229>
[32] [CITE@en[XSLT 2.0 and XQuery 1.0 Serialization (Second Edition)]]
( ([TIME[2010-12-17 00:08:20 +09:00]] 版))
<http://www.w3.org/TR/2010/REC-xslt-xquery-serialization-20101214/#uri-escaping>
[33] [CITE@en[XSLT 2.0 and XQuery 1.0 Serialization (Second Edition)]]
( ([TIME[2010-12-17 00:08:20 +09:00]] 版))
<http://www.w3.org/TR/2010/REC-xslt-xquery-serialization-20101214/#XHTML_ESCAPE-URI-ATTRIBUTES>
[34] [CITE@en[Web Applications 1.0 r1835 Don't escape '%' when doing URL resolution. (bug 5802) (credit: hs)]]
( ([TIME[2008-07-01 08:52:00 +09:00]] 版))
<http://html5.org/tools/web-apps-tracker?from=1834&to=1835>
[35] [CITE@EN[XQuery 1.0 and XPath 2.0 Functions and Operators (Second Edition)]]
( ([TIME[2010-12-17 00:06:54 +09:00]] 版))
<http://www.w3.org/TR/2010/REC-xpath-functions-20101214/#func-encode-for-uri>
[41] [CITE@en[Bug 24257 – "Percent-decoding + full-width characters + percent decoding" for domains is missing]]
( ([TIME[2014-01-15 09:00:30 +09:00]] 版))
<https://www.w3.org/Bugs/Public/show_bug.cgi?id=24257>
[42] [CITE@en[309671 – Support %-escaped hostnames per RFC 3986 (3.2.2) / Cannot open IDN from other applications(e.g., from Thunderbird)]]
( ([TIME[2014-01-15 09:02:05 +09:00]] 版))
<https://bugzilla.mozilla.org/show_bug.cgi?id=309671>
[93] [CITE@en[XSLT and XQuery Serialization 3.0]]
( ([TIME[2014-04-07 23:19:15 +09:00]] 版))
<http://www.w3.org/TR/xslt-xquery-serialization-30/#uri-escaping>
[94] [CITE@en[XSLT and XQuery Serialization 3.0]]
( ([TIME[2014-04-07 23:19:15 +09:00]] 版))
<http://www.w3.org/TR/xslt-xquery-serialization-3/#uri-escaping>
[102] [CITE@en[draft-montenegro-httpbis-uri-encoding-00 - Deterministic URI Encoding]]
( ([TIME[2014-10-19 06:34:47 +09:00]] 版))
<https://tools.ietf.org/html/draft-montenegro-httpbis-uri-encoding-00>
[68] [CITE@en[Encode { and } in username/password/path to fix #16. Also acknowledge… · whatwg/url@c296e2f]]
([TIME[2015-06-11 12:55:46 +09:00]] 版)
<https://github.com/whatwg/url/commit/c296e2f8519a1d6614d664708d368a342682c9a1>
[69] [CITE@en[Do not encode ` in query. Fixes #17. · whatwg/url@f54c44d]]
([TIME[2015-06-11 12:55:59 +09:00]] 版)
<https://github.com/whatwg/url/commit/f54c44d1f790dd2b8913e955ba6d334c6f14a048>
[FIG(quote)[
[FIGCAPTION[
[70] [CITE@en[RFC 2718 - Guidelines for new URL Schemes]]
([TIME[2015-05-17 15:15:23 +09:00]] 版)
<https://tools.ietf.org/html/rfc2718#section-2.2.5>
]FIGCAPTION]
> Unless there is some compelling reason for a
particular scheme to do otherwise, translating character sequences
into UTF-8 (RFC 2279) '''['''3''']''' and then subsequently using the %HH
encoding for unsafe octets is recommended.
]FIG]
[72] [CITE@en[Add a section on rendering URLs with some advice around bidirectional… · whatwg/url@d1152b9]]
([TIME[2015-08-27 11:53:15 +09:00]] 版)
<https://github.com/whatwg/url/commit/d1152b94a16ae91e1f72d128fd5ef589635f0e7c>
[105] [CITE@en[Fix #77: always decode "%2e" in a URL's path · whatwg/url@bee5ad8]]
([TIME[2016-01-15 18:56:05 +09:00]] 版)
<https://github.com/whatwg/url/commit/bee5ad8041adfe6fc676c527bbc3f3cf4562ef67>
[106] [CITE@en[Drop dependencies on Encoding Standard's decoder concept · whatwg/url@37f9329]]
([TIME[2016-02-11 12:00:52 +09:00]] 版)
<https://github.com/whatwg/url/commit/37f932928378c0df521034cfd223f4ba603ef476>
[107] [CITE@en[Use the "get an output encoding" from the Encoding Standard · whatwg/url@a9197f7]]
([TIME[2016-02-11 12:03:28 +09:00]] 版)
<https://github.com/whatwg/url/commit/a9197f7714e6b125f1f760ca1aa661530261773c>
[139] [CITE@en[Bug 157153 – Accessing form.action as property URI-encodes spaces but not curly braces]]
( ([TIME[2016-05-27 10:28:24 +09:00]]))
<https://bugs.webkit.org/show_bug.cgi?id=157153>
[157] [CITE[技術/HTTP/URLエンコードで 0x20(スペース) を "+" にすべきか "%20" にすべきか - Glamenv-Septzen.net]]
([TIME[2016-06-24 20:24:28 +09:00]])
<http://www.glamenv-septzen.net/view/1170>
[FIG(quote)[
[FIGCAPTION[
[170] [CITE@en[PHP: http_build_query - Manual]]
([TIME[2016-06-24 13:08:18 +09:00]])
<http://php.net/manual/en/function.http-build-query.php>
]FIGCAPTION]
> enc_type
> By default, PHP_QUERY_RFC1738.
> If enc_type is PHP_QUERY_RFC1738, then encoding is performed per » RFC 1738 and the application/x-www-form-urlencoded media type, which implies that spaces are encoded as plus (+) signs.
> If enc_type is PHP_QUERY_RFC3986, then encoding is performed according to » RFC 3986, and spaces will be percent encoded (%20).
]FIG]
[179] [CITE@en[Percent encode fragments too]]
([[annevk]]著, [TIME[2016-12-10 02:43:08 +09:00]])
<https://github.com/whatwg/url/commit/373dbedbbf0596f723ce8a195923da98b698aeb0>
[180] [CITE@en[Stop decoding all %2e's in paths]]
([[annevk]]著, [TIME[2016-10-28 20:46:47 +09:00]])
<https://github.com/whatwg/url/commit/fbff6834a8a03576261f777d0e0afea5c1bc5a09>
[181] [CITE@en[Percent-decode more stuff? · Issue #87 · whatwg/url]]
([TIME[2017-01-05 20:17:02 +09:00]])
<https://github.com/whatwg/url/issues/87>
[189] [CITE@EN[XPath and XQuery Functions and Operators 3.1]]
([TIME[2017-03-21 16:02:06 +09:00]])
<https://www.w3.org/TR/2017/REC-xpath-functions-31-20170321/#func-encode-for-uri>
[190] [CITE@en[XSLT and XQuery Serialization 3.1]]
([TIME[2017-03-20 12:35:18 +09:00]])
<https://www.w3.org/TR/2017/REC-xslt-xquery-serialization-31-20170321/#uri-escaping>
[191] [CITE@en[Web Applications 1.0 r6112 clarify how to generate ifragment thingies]]
( ([TIME[2011-05-07 07:34:00 +09:00]] 版))
<http://html5.org/tools/web-apps-tracker?from=6111&to=6112>
[192] [CITE@en[Define percent decoding of strings]]
([[annevk]]著, [TIME[2017-10-10 21:45:20 +09:00]])
<https://github.com/whatwg/url/commit/e172261b0946036b485322976f93f50707159ce3>
[197] [CITE@en[data URLs: revised specification · Issue #234 · whatwg/fetch]]
([TIME[2017-10-11 23:05:39 +09:00]])
<https://github.com/whatwg/fetch/issues/234>
[198] [CITE@en[Define percent decoding of strings by annevk · Pull Request #340 · whatwg/url]]
([TIME[2017-10-11 23:09:57 +09:00]])
<https://github.com/whatwg/url/pull/340>
[FIG(quote)[