2021年11月22日
川俣晶の縁側ソフトウェアnew_C#入門・全キーワード明快解説! total 784 count

文字型と文字列型: 文字と1文字の文字列は何が違うの?

Written By: 川俣 晶連絡先

この章のテーマ §

文字型/文字列型 (char/string型)についての基礎を扱います。

前提知識 §

Console.WriteLineメソッド, 文字列の基礎, 変数の基礎, キャスト

解説 §

 文字型(char型)と文字列型(string型)の関係は歴史的な理由により、かなりの迷宮になっています。

 ここをクリアできればC#がチョット分かると自慢できます。

 さて、基本的に文字型(char型)は1文字を表すデータ型です。その1文字を複数並べたのが文字列型(string型)です。ですから【A】は1文字ですが、【エース】は3文字の文字列です。

 文字と文字列は異なるデータ型なので相互に代入などはできません。

 しかし、文字列をforeach文などで列挙すると、複数の文字が得られます。

 サンプルソースのforeach文を確認して下さい。

 文字列にはLengthプロパティがあって、文字列の長さを取得できます。【エース】なら3が得られますが、【ダイヤのエース】なら7が得られます。

 しかし、char型にLengthプロパティはありません。長さは1に決まっているからです。

 その代わり、char型はキャストなどを使うと整数型に変換できます。文字にはUnicodeの番号があるので、その番号を取得できるのです。

 この番号は国際標準のUnicodeコンソーシアムが標準化して規格書を発行しているので、C#以外の技術であっても、同じUnicodeに準拠している限り同一です。JavaでもXMLで同じ番号が使用されます。

 さて、ここからが難題です。

 Unicodeの番号は基本的に16bitなので最大65536種類の文字にしか番号を与えられませんが、これでは全世界の文字を収容するには到底足りません。特に、多くの漢字を必要とする中国台湾の勢いや、種類の多い顔文字の増加などは著しいものがあります。

 それらの文字は、実は【2文字分で1文字を表す】サロゲートペアという技術で表現されており、文字型(char型)では1文字を表現できません。文字列型(string型)なら扱えますが、Lengthプロパティで長さを取得すると2になります。

 サンプルソースに絵文字の長さが2になっているケースが含まれるので参考にして下さい。

豆知識・2文字で1文字の不思議な文字が生まれた理由 §

 1文字7bitあれば足りるアメリカ人の多くは文字のビット数を増やすことに反対していました。全世界の文字を収容する文字セットを作成することになったとき、アジア圏の技術者は全員16bitでは足りないと言いましたが、アメリカ人は16bitより大きい文字セットは許容できませんでした。だから、Unicodeは16bitという前提で設計されましたが、当然のように16bitでは足りずに後から拡張されました。これが、2文字で1文字を表すサロゲートペアという技術です。C#等の古いルーツを持つ技術は、【全ての文字が16bitで表現できる】という前提がまだ存在する時代に設計されているので、文字型(char型)には16bitしか割り当てていません。これを拡張すると互換性の問題が発生するので、2文字で1文字を表す不思議な仕様がまだ残っています。ただし、アルファベット、数字、基本的な記号、ひらがな、カタカナ、基本的な漢字でこれに引っかかるケースは少なく、人名や絵文字を使わなければあまり意識する必要はないとも言えます。

罠の数々 §

  • 2文字分で1文字を表している場合、容易に2つの1文字に分解できるが、それでは文字としての情報が残らないのでデータがゴミになる。このような分割をしてしまわないように注意しよう
  • 文字の形は出力デバイスごとに変化する。コンソールに出力するとニコニコマーク😀は、かなり潰れた白いマークになってしまう
  • コンソールの文字列は通常互換性の問題からCP932という古いコード体系になっていて新しい文字を表示できない。サンプルソース先頭のConsole.OutputEncoding = System.Text.Encoding.UTF8;は、これをUnicodeに切り換えるおまじないである
  • 基本的に、文字の定数は'で、文字列の定数は"でくくる。間違えると型が違って予想通りに振る舞ってくれない

参考リンク §

文字列 (C# プログラミング ガイド)

char (C# リファレンス)

サンプルソース: kw_char_string §

Console.OutputEncoding = System.Text.Encoding.UTF8;

string s1 = "あ";    // これは文字列です

char c1 = 'あ';   // これは文字です。

Console.WriteLine($"{s1} 長さ{s1.Length}");

Console.WriteLine($"{c1} 番号{(int)c1}");

string s2 = "😀";

//char c2 = '😀';    // エラーになります

Console.WriteLine($"{s2} 長さ{s2.Length}");

string s3 = "LUCKY";

foreach (char ch in s3) Console.WriteLine($"{ch} 番号{(int)ch}");

実行結果 §

あ 長さ1

あ 番号12354

😀 長さ2

L 番号76

U 番号85

C 番号67

K 番号75

Y 番号89

リポジトリ §

https://github.com/autumn009/CSharpPrimer2

練習問題 §

 以下のプログラムの実行結果を予測してみよう。

char a = ' ';

string b = " ";

if( a == b ) Console.WriteLine($"一致");

  1. true
  2. 一致を出力する
  3. 何も出力しない
  4. 実行させると例外が起きる
  5. コンパイルできない

[[解答]]

単行本情報