「住所は英数字もすべて全角で入力してください」はなぜそうなったのか

Webサービスのフォームに住所を入力するとき、丁目や番地などを入れる欄について、数字やハイフンを全角で書かなければいけない「全角縛り」をやっているフォームをよく見ます。半角文字を入力してしまってエラーになったり、咄嗟に変換方法を思い出せなかったり、全角と半角の見分けが付きづらかったり、「全角縛り」であることが明示されていなかったり、「ハイフン」としてどの文字を使うべきかわからなかったり……と、鬱陶しさを感じることが多くあります。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">「住所は全角のみ」（数字やハイフンも絶対に半角を受け付けない）という仕様がどういう経緯で生まれて、どう広まっていったのかが気になってる。いま存在しているのは過去の仕様や慣習の踏襲として理解できても、そもそもなぜそれらが生まれたのかが理解できない。 <a href="https://t.co/ZLz0Pw9GOK">https://t.co/ZLz0Pw9GOK</a></p>&mdash; ymrl (@ymrl) <a href="https://twitter.com/ymrl/status/1817876115047981483?ref_src=twsrc%5Etfw">July 29, 2024</a></blockquote> 

これについて「そもそも何故そういう仕様が生まれたのかわからないな」と思ってTwitter[^1]に書いてみたところ、いろいろな反響がありました。そこでいろいろ知らなかったことを学べたり、それがキッカケでいろいろ調べたりしたので、その内容をまとめます。

[^1]: Xという名前が気にいらないので、いまだに「Twitter」とか「ツイート」と呼んでいます。


## 筆者の考えについて

先に、筆者の立場を明確にしておきます。私は、現在稼動しているものに関しては、どんなアプリケーションであっても住所の欄について「全角縛り」をするべきではない、そもそも不必要に文字種の制限をするべきではないと考えています

いまどき、ユーザーはパソコンからも、スマートフォンからも、Webサービスをはじめとするアプリケーションを利用します。ハードウェア、OS、日本語入力システム、表示に使われるフォントなど、多様な環境で使われます。しかも同じ人がいろんな端末を使っていたり、気軽に買い替えたりもします。

PCなら半角・全角の切り替え方がわかるがスマートフォンではわからないとか、この端末では半角と全角が見分けづらいとか、新しく買った端末やインストールしてみた日本語入力ソフトではわからないとか、同僚や家族にやり方を聞かれたが端末のOSが違うのでわからないとか、そういうことが起こりやすくなっています。

ユーザーも多様になっています。昔ならパソコンの入門書や授業や研修で全角文字・半角文字といった概念を学んできたユーザーばかりだったかもしれません。今はそういう決まった入口でないところからも老若男女がITを使いはじめています。あるいは、日本語にあまり馴染みのない海外出身のユーザは、彼らの言語圏にはない全角文字・半角文字についてよくわからないということもありそうです。
全角文字・半角文字といった概念を必ずしもユーザーが習得しているわけではありません。

UTF-8（やShift_JISやEUC-JP）では、半角英数字から全角英数字へのプログラム上での変換は非常に簡単です。もし英数字を全角で保存しなければならない都合があったとしても、ユーザーが入力した文字列のうち半角英数字の部分を全角英数字に変換してから保存してしまえばいいのです[^6]。

[^6]: 記事公開当初は「半角文字から全角文字への変換」としていましたが、対象を英数字に絞る表現へと改めました。いわゆる半角カタカナと、Shift_JISやEUC-JPでASCIIコードに含まれる記号文字を対象とする場合、変換ロジックがやや複雑になります。


「全角縛り」は、様々な技術的・コスト的制約があり、ユーザーの端末の種類もITへのリテラシーの高さもある程度限定されている中で、当時の開発者なりの最適解だったのかもしれません。当時の状況での選択に関して悪く言うつもりはありません。しかし今となっては、時代遅れで、無くしていくべきものだと私は思います。

特定の文字種別が必要であれば自動的に変換をするなどをして、システムの都合でユーザーに不便を押し付けることがないようにしたい、あるいはしてもらいたいものです。

そういうことを言っていく、改善を働きかけていくためにも「なぜ全角が良いとされているのか」「なぜ今までその仕様が残りつづけているのか」を知ることには意味があると思っています。そういうつもりで、いろいろと調べたりしていました。

## 印刷のため説

最初になるほどと思ったのが、印刷のレイアウトが計算しやすいから、という話です。窓付き封筒のような限られた文字数しか表示できない場所に印字するとき、全角に統一することで改行位置の調整がやりやすくなるというのです。ほかにも、印刷や表示まわりのいろいろな経験談や、「こうではないか」という推測を見かけました。

- 原稿用紙や方眼紙に印刷をしたい
- ぴったり収まるような改行位置を推測しやすい
- 縦書き印刷すると半角英数字が横向きにされてしまう

私も文字列がレンダリングされた時の幅や高さ、改行位置、特定の幅にぴったり収まる文字数を計算したいという状況に頭を悩ませたことが何度もあります。いわゆる全角文字に限定されていれば、「半角文字2つで全角文字1つぶん」のような計算やレイアウトをしなくて済みます。

## JIS X 0208 だから説

何人かが指摘していたのが「JIS X 0201とJIS X 0208が独立した規格だから」というものでした。

アメリカの国内規格としての[ASCIIコード](https://ja.wikipedia.org/wiki/ASCII)、国際規格としての[ISO/IEC 646](https://ja.wikipedia.org/wiki/ISO/IEC_646)を元に、日本語を扱うためのカタカナなどの文字を追加した規格が[JIS X 0201](https://ja.wikipedia.org/wiki/JIS_X_0201)[^2]でした。ASCIIコードと重複する部分のなかでは、バックスラッシュが円記号に、チルダがオーバーライン記号に変更されています。

[^2]:策定当初はJIS C 6220でしたが、のちに現在使われている規格番号に変更されています。ややこしいのでこの記事では現在使われている規格番号に統一します。

ASCIIコードはすべての文字が7ビットの範囲に収まりますが、JIS X 0201ではカタカナを扱うため7ビットの範囲には収まらず、1文字を8ビットで表現するか、7ビットでは制御文字を使ってラテン文字の集合とカタカナの文字集合を切り替えるようになっているようです[^3]。

[^3]: 「1バイトは8ビット」が正式に定義されたのは2008年の[IEC 80000-13](https://ja.wikipedia.org/wiki/IEC_80000-13)で、特に昔のコンピューターの世界は6ビットだったり7ビットだったりが混在していたので、今回紹介しているJIS規格でも7ビットの処理系が考慮されている、という話も今回の本筋とはあんまり関係がないですが、注釈くらいは必要かもしれません

カタカナ以外の、より広い文字を定義した文字集合の定義が[JIS X 0208](https://ja.wikipedia.org/wiki/JIS_X_0208)です。JIS X 0208は、1文字が7ビットまたは8ビットを1バイトとして2バイトぶんの情報量を持ち、数字・ラテン文字・ひらがな・カタカナ・漢字・ギリシャ文字・キリル文字など幅広い文字が含まれます。

JIS X 0208の定義する文字集合を、実際のビット列にする符号化方式は複数あり、そのなかにはJIS X 0201の使用する領域と被ってしまうものがありました。文字の符号化では、7ビットまたは8ビットで表現できる `0x00` から `0xFF` （7ビットの場合は `0x00` から `0x7F`）の範囲がCL、GL、CR、GR領域と呼ばれます。

- CL領域: `0x00` から `0x1F` 
- GL領域: `0x21` から `0x7E`
- CR領域: `0x80` から `0x9F`
- GR領域: `0xA1` から `0xFE`

上記には `0x20` `0x7F` `0xA0` `0xFF` が抜けています。`0x20` と `0x7F` はASCIIコードで `SPACE` と `DELETE` に割り当てられていた場所で、JIS X 0208のどの符号化方式でも同じく `SPACE` と `DELETE` が割り当てられています。 `0xA0` と `0xFF` は、8ビットでの `0x20` と `0x7F` の先頭ビットの値を `1` にした値です。

ASCIIコードでは、GL領域にすべての文字が入っています。また、JIS X 0201の8ビット表現では、カタカナがGR領域に配置されています。ASCIIコードでは、CL領域に制御文字が入っていました。JISではJIS X 0211に制御文字が定義され、C0集合と呼ばれる制御コードはASCIIと同じでCL領域に、8ビットの場合はC1集合と呼ばれる制御コードがCR領域に割り当てられる場合があります。

つまり、CL領域と、C1集合の制御文字を使う場合はCR領域をよけて、おもにGL領域とGR領域を文字集合に割り当てていくことになります。そのため、GL領域を使えばASCIIコードの文字と、GR領域を使えばJIS X 0201のカタカナと競合してしまいます。JIS X 0208に定義されている符号化方式で、文字集合を素直にマッピングしたような構成の「漢字用7ビット符号」や「漢字用8ビット符号」はGL領域を使い、ASCIIやJIS X 0201の文字と共存できません。

のちにインターネットの時代には、EUC-JPやShift_JISのようにJIS X 0201とJIS X 0208を混在させられる符号化方式が普及して、当たり前に両方の文字を使えるようになりました。EUC-JPはJIS X 0208の文字の表現にGR領域を使用し、JIS X 0201のカタカナは1バイト目を `0x8E` にした2バイトコードになっています。Shift_JISは第一バイトにCR領域とJIS X 0201が使用していない部分を、第二バイトではCL領域や `0x7F`を除いた部分を使って、共存できるようにしています。これらの符号化方式は、後からJIS X 0208に符号化方式として追加されています[^4]

[^4]: ただしEUC-JPは、JIS X 0201のカタカナやJIS X 0212の補助漢字など、JIS X 0208に存在しない文字を抜いた状態でJIS X 0208に追加されています

このとき、JIS X 0208由来の文字がいわゆる「全角文字」、JIS X 0201由来の文字がいわゆる「半角文字」となりました。さらにそれがすべての言語の文字を扱おうとするUnicode文字集合や、その符号化方式であるUTF-8にも引き継がれ、今に至ります。

そんななか、JIS X 0201と混在できないJIS X 0208の符号化形式を使用しているシステムとデータを共有したり、その時代のデータ形式を継承していたり、あるいはバックエンドそこから接続されたどこかでそういったシステムが現役で動いていたりして、「全角縛り」が発生してしまっているというのです。

## 制御文字を挟みたくなかった説

JIS X 0208とJIS X 0201のように文字集合が複数あったり、7ビットでJIS X 0201を使用する場合などは、限られたビット数のなかで複数の文字集合を切り替えて使う必要があります。そこで、制御文字をはさんで文字集合を切り替えながら文字列にしていくという符号化方式が考案されました。

たとえば7ビットでJIS X 0201を使用する場合には、 `0x0E` (SHIFT IN)より後ろの文字はカタカナ、`0x0F` (SHIFT OUT) よりも後ろの文字はラテン文字、という切り替え方をしていたようです。

制御文字を挟む方式で、日本で最も普及していそうなのが[ISO-2022-JP](https://ja.wikipedia.org/wiki/ISO-2022-JP)で、長いこと電子メールで使用されるエンコードとして親しまれてきました。ISO-2022-JPでは、`0x1B` (ESC) から始まるエスケープシーケンスを挟むことで文字集合を切り替えています。このエスケープシーケンスは3バイトあり、半角と全角を切り替えるたび3バイト消費してしまうのを避けたかったので「全角縛り」をしているのではないか、というのです。

また、専門外で調べきれていませんが、こういった制御文字を使用した文字種別の指定は[メインフレーム](https://ja.wikipedia.org/wiki/%E3%83%A1%E3%82%A4%E3%83%B3%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0)でも採用していることが多いようです。メインフレームではEBCDICという、ASCIIとは全く由来を別とする文字コード体系が採用されており、ベンダーごとに日本語化拡張を開発した歴史があるようです。Wikipediaの[漢字シフトコード](https://ja.wikipedia.org/wiki/%E6%BC%A2%E5%AD%97%E3%82%B7%E3%83%95%E3%83%88%E3%82%B3%E3%83%BC%E3%83%89)のページには、ISO-2022-JPのほか、ベンダー独自の漢字シフトコードも紹介されています。

制御文字によって文字集合を切り替える方式は、制御文字のせいで見た目よりも長いバイト列を使ってしまう以外にも、部分文字列を切り出したり文字数をカウントしたりするときにも考慮が必要そうで、システム設計時には文字種をなるべく混ぜたくない気持ちになりそうです。

## COBOLが原因説

これもまた専門外ではあるのですが、原因としてCOBOLがあるのでは、と指摘する人も多くいました。COBOLではしばしば固定長のレコードが使われ、またデータ型の定義の時点で2バイトコードに固定されてしまうらしいのです。

具体的には、`PICTURE` (`PIC`) 句によってデータ型を宣言する際、`N` を使用すると「国別項目」や「日本語項目」と呼ばれるデータとなり、これが2バイト固定だというのです。これらは日立製作所やIBMのドキュメントで確認できます。

- https://itpfdoc.hitachi.co.jp/manuals/3020/30203D45Q0/GD450025.HTM
- https://www.ibm.com/docs/ja/cobol-zos/6.4?topic=clause-symbols-used-in-picture

こういった場所には、いわゆる半角英数字のような1バイト文字を含む文字列を保存できず、住所のように漢字・ひらがな・カタカナと数字が混在するデータに対しては「全角縛り」をするしかない、というのです。

## 関係なさそうな説

ほかにもいろいろな説が寄せられましたが、おそらく関係ない、または大きな要因ではないだろうと思ったものもありました。

### SQLインジェクションを防ぐため説

「SQLインジェクション対策ではないか」という人がそれなりの数いました。が、これは私は無関係、もしくは関係があったとしても「ついで」程度のもので関連性は薄いと思っています。

SQLインジェクションは、SQLベースのデータベースを利用しているサービスに対し、SQL文がシステムの意図通りに動かず、別のデータを書き換えたり読み込んだりしてしまうのを狙う攻撃手法です。SQLインジェクションは大抵、記号文字のエスケープ漏れを狙うはずです。それらは半角文字が使われるので「全角縛り」がSQLインジェクション対策になるということのようです。

たしかに「全角縛り」をしているフィールドではSQLインジェクション攻撃は成立しないでしょう。しかし、もしSQLインジェクション対策として「全角縛り」をしているとしたら、すべての入力欄で「全角縛り」をしなければなりません。そんなことは現実的に可能でしょうか……？

すべての入力欄で「全角縛り」をするということは、ユーザーが担当者への要望を記載したり、配送時の置き配指示をしたりする自由入力欄も全角で書かなければいけないということです。これは住所の「全角」縛りよりもさらに鬱陶しいはずです。メールアドレスやURLも全角で書かなければいけなくなるかもしれません。全角に直されたメールアドレスやURLは、もはやそれはメールアドレスやURLと言えるものではないでしょう[^5]。

「SQLインジェクション対策」という名前のもとに「全角縛り」をやる前に、もっと本質的な対策をする必要があるはずです。

とはいえ、本質的な対策が漏れていた箇所がたまたま「全角縛り」だったおかげで被害が起きずに済んでいるというケースはあるかもしれません。あるいは、「全角縛り」のされている箇所にはSQLインジェクションやXSSを発生させる文字列を入れる方法がないため、脆弱性診断やQAテストの工数を減らせている効果はあるかもしれません。そういう意味で「ついで」程度の関係はあるかもしれない、とは思っています。

[^5]: 新聞社や通信社がWebに載せるニュースでのURLの記載方法について、その中の方々にはぜひ考えてみてほしい問題でもあります。

### 検索・突合・ソートのため説

「入力された住所を検索したり、他のデータと突き合わせたりするために全角に絞っているのではないか」という意見もありました。しかしこれも、あまり関係がないと思っています。

日本の住所データは表記ゆれが非常に起きやすい性質があります。「全角縛り」をやっていようがいまいが、表記ゆれが起きてしまいます。たとえば、丁目・番地・号が「1-2-3」の住所を書くにしても、

- 1-2-3
- 1の2の3
- 1丁目2番地3
- 1丁目2-3
- 一丁目二番地三

のように、いろいろな書き方ができてしまいます。漢数字とアラビア数字を混ぜたりすると組み合わせはもっと増えます。「全角縛り」をやったところで、表記ゆれの激しいデータが生まれてしまうのです。検索・突合・ソートを行うのであれば、まずこの表記ゆれを解消するところからやらなければなりません。その難しさは半角英数字が紛れ込んだところでそんなに変わらないように思えます。

もうひとつ気になるのは、「そもそも住所を、丁目・番地・号のレベルで検索する需要はそんなに存在するのか？」というところです。

住所の「全角縛り」をやっているのは会員登録フォームだったり荷物の配送先の指定フォームだったりするわけで、おそらく顧客データや注文データと紐付けて管理することになるでしょう。では、顧客データや注文データを、住所で検索するニーズはあるのでしょうか？「特定の地域へ営業をかけるため、○○県の顧客リストが必要」とか「配送ドライバーに、××市の注文データの配達を依頼する」のように、都道府県や市区町村、もしかしたら町名くらいのレベルで検索するニーズが生まれたり、そういう業務フローが組まれていることは考えられます。しかし丁目・番地・号のレベルの検索というのはどうもピンと来ないのです。そういうことができる状況では、おそらく他の情報も手元にあって、他の情報をキーにして検索したほうがよさそうに思うのです。


<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>