読者です 読者をやめる 読者になる 読者になる

「文字列」について

「文字列を文字の列とみなす単純化」について議論がありますが、前提が抜け落ちてるように思うので書くことにします。
そもそもこの話はどのような文脈の上にあるかというと、テキスト処理 (wikipedia:en:Text_processing) の文脈になります。ここでいう「テキスト処理」とは plain text (wikipedia:プレーンテキスト) の検索・加工のことで、ここでは特に UNIX Text Processing の系譜が念頭に置かれています。つまり、複雑な装飾を含むリッチテキストではなく、処理の対象を ASCII 文字列といくつかの制御文字へと抽象化することで、正規表現のような強力な道具を用いた処理を可能とした世界です。UNIX でのお話ですから、ここでの具体的な処理の単位は char であり、全体としては char[] になります。この char の中身は上で述べたとおり、ASCII 文字列ですので、値は 0〜127 です。 (実際初期の UNIX tools は 8-bit clean ではなかった)
さて、アメリカ以外の国では (制御文字込みで) 128文字では当然足りないので、拡張が必要です。拡張の方針には

  1. ワイド文字: char を大きくする
  2. マルチバイト文字: 複数の char を組み合わせて、「1文字」にする

の2種類があります。

ワイド文字

ヨーロッパの言語ではアクセント付き文字を用いますが、これらは ASCII に収録されていません。ここで取られた策が 7 bit から 8 bit への拡大です。とはいえ、UNIX において char はたいていの場合もとから 8 bit でしたから、何か新しいものを導入する必要はありませんでした。
その後の wchar_t の導入や TRON コード、Unicode などでは、16 bit や 32 bit などのより大きな文字型が提案されています。
ワイド文字のメリットはプログラミングモデルはそのままで、型と関数だけを取り替えればより多くの文字を扱えることです。これにより、文字コードのことをよく知らない人でも国際化に対応したプログラムを開発出来ます。
デメリットは文字列型が変更されることでプログラムのインターフェイスが変わってしまうことと、ネットワークなどのバイトストリームではそのままでは使えないことになります。

マルチバイト文字

複数の char を組み合わせて一つの文字を表すようにするのが「マルチバイト文字」です。
この方法の場合、プログラミングモデルは複雑になりますが、既存のプログラムを改修する際に、そのインターフェイスを維持したまま文字数を数えるなど必要最小限の変更で日本語対応させられることがメリットでした。

世界の選択

当初、世の人々はワイド文字こそが長期的には正しい選択だと考えていました。1 wchar_t = 1文字 は明らかにわかりやすい抽象化だからです。また、単一の文字コードUnicodeを採用することで国際化は格段に容易となることが期待されました。しかし、この考えは何重にも間違っていたことがその後明らかになります。
まずはUnicodeが16bitに収まらなかったことです。これにより、シンプルな16bit固定長だったUCS-2サロゲートペアを用いたUTF-16へと改修せざるを得なくなり、複数のワイド文字 (code unit) で1文字を表すという両者のデメリットを兼ね備えた方式へと堕ちました。
また、Unicodeが本格的に IVS をはじめとした文字の結合を扱うようになったことにより、複数Unicode文字 (Unicode scalar value) を組み合わせて1文字を表す「結合文字列」を扱う必要が増し、結局プログラミングモデルの変更も必要となってしまいました。(なお、絵文字周りの話は文字コードのかなり上の方のレイヤーの話なので、今回の話とは全く関係ないと思う)
加えて、現代の多くのプログラムではネットワーク通信を扱うことが多いと思いますが、というかHTTPを扱うと思いますが、HTTPはASCIIをベースとしつつバイナリも混ざる混沌とした世界です。言い換えるとバイト列なのに文字列処理をしたいというワイド文字としては扱いづらいデータになります。(byte[]とStringの相互変換が乱れ飛ぶコードを目にした人も多いのではないでしょうか)
結局の所、ワイド文字という試みは失敗であったし、今となっては無意味な複雑化であると結論づけていいのではないかと思います。

Ruby の選択

Ruby はマルチバイト文字型のアプローチを採用しています。(「Unicode路線に見切りをつけ」たのではなく、ワイド文字路線に見切りを付けたのである)これにより、String#each_byte, String#each_char, String#each_codepoint などのメソッドを通じて、バイト列上に構築された様々な抽象化レイヤを自在に扱うことが出来ます。また、現在は要望やユースケースが皆無なために実装は見送っていますが、IVS を考慮した each 亜種も容易に追加することが出来ます。
加えて、複数文字コードを同時に扱う…ことは普通の人はやらないと思いますが、ネットワークプログラミングを行う際にバイト列と文字列をシームレスに扱うことが出来ます。つまり、HTTPヘッダ等を扱う際にbyte[]とStringを変換する必要がありません。

まとめ

ワイド文字はオワコン