Menu

C#の文字列:長さ、比較、エスケープ文字、逐語的文字列、複数行の文字列

C#の文字列の仕組みを解説します。不変性、Lengthとインデックス、==、Equals、string.Compareによる比較、エスケープ文字、逐語的な@文字列と複数行のテキスト、生文字列リテラル、nullと空のチェック、そして文字のループを扱います。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

C#の string は、UTF-16の文字(char の値)の並びです。参照型ですが、重要な点では値のように振る舞います。不変であり、== はオブジェクトの同一性ではなくテキストを比較します。string は.NETの型 System.String を表すC#のキーワードで、2つは互換です。

出力:

Porto
5
0
*****
hi
Porto has 5 letters

文字列は不変

文字列をその場で変更するメソッドはありません。ToUpper、Replace、Trim をはじめすべてのメソッドは新しい文字列を返し、元の文字列には触れません。戻り値を使い忘れるのは、非常によくある文字列のバグです。

出力:

[  maya  ]
[MAYA]
tea
tea and cake

不変なので、文字列はスレッド間で安全に共有でき、辞書のキーにも使えます。代償として、ループの中で文字列を少しずつ組み立てると、ステップごとに新しい文字列が作られます。その場合はStringBuilderを使います。

長さとインデックス

Length はプロパティです(かっこは付けません)。インデクサー s[i] は、0から数えた位置 i の char を返し、読み取り専用です。

出力:

7
A
1
IndexOutOfRangeException
XB-2931

C# 8以降では、末尾からのインデックスも使えます。code[^1] は最後の文字で、code[code.Length - 1] と同じです。複数の文字をまとめて取り出すには、Substringを参照してください。

Length が数えるのはUTF-16のコード単位で、読む人が文字と見なすものではありません。ほとんどの言語のほとんどの文字は1単位ですが、絵文字や一部のまれな文字体系は2単位(サロゲートペア)を使うので、"👍".Length は2です。

文字列の比較

== と != は、大文字と小文字を区別して文字を比較します。それ以外の比較では、比較方法を示す StringComparison の値を渡します。

出力:

False
False
True
True
apple first
True
False
False
True

どの比較を使うか:

  • Ordinal は文字の数値コードを比較します。高速で予測でき、どのマシンでも同じです。識別子、ファイル名、キー、プロトコルの値など、人に見せないものすべてに使います。
  • OrdinalIgnoreCase は同じ比較で、大文字と小文字を無視します。ファイルの拡張子やHTTPヘッダーのような、大文字と小文字を区別しない確認に適しています。
  • CurrentCulture(と CurrentCultureIgnoreCase)はユーザーの言語の規則に従うので、その言語の辞書と同じように単語を並べます。ユーザーに見せるリストの並べ替えに使います。
  • InvariantCulture は、ユーザーの言語設定に依存しない、カルチャを考慮した規則を使います。それでもグローバリゼーションのライブラリには依存します。現代の.NETはICUを、.NET FrameworkはWindowsのNLSを使い、両者は一部の文字列の順序が異なることがあります。

StringComparison なしの string.Compare(a, b) と a.CompareTo(b) は現在のカルチャを使うので、結果がマシンによって変わります。a.ToLower() == b.ToLower() による比較は避けます。新しい文字列を2つ割り当て、一部のカルチャでは間違った答えになります(トルコ語の点付きと点なしのiが典型的な例です)。

a がnullのときに a.Equals(b) を呼ぶと NullReferenceException が投げられますが、a == b と静的な string.Equals(a, b) はnullを安全に扱います。

エスケープ文字

通常の文字列リテラルの中では、バックスラッシュがエスケープシーケンスを始めます。

エスケープ意味
\n改行(ラインフィード)
\r復帰(キャリッジリターン)
\tタブ
\\バックスラッシュ
\"二重引用符
\'単一引用符(char のリテラルでのみ必要)
\0null文字
\uXXXX4桁の16進数のコードによるUTF-16の文字。例:\u00e9
\U0001F44D8桁の16進数のコードによる文字。U+FFFFを超えると2つの char 値になる
\xH から \xHHHH1から4桁の16進数による文字(長さがあいまいなので避ける)
\a、\b、\f、\v警告音、バックスペース、改ページ、垂直タブ
\eエスケープ文字、U+001B(C# 13)

出力:

Name:	Ana
City:	Porto
She said "hi"
C:\temp\report.txt
cafe = cafe
' is a quote char

"\d" のような認識されないシーケンスはコンパイルエラー(CS1009、認識できないエスケープシーケンス)になります。Windowsのパスや正規表現を通常の文字列で書くと、こうなります。

逐語的文字列と複数行の文字列

リテラルの前に @ を付けると逐語的になります。バックスラッシュはそのままの文字になり、文字列は複数行にまたがれます。残るエスケープは、二重引用符1つを生成する "" だけです。

出力:

C:\Users\ana\Documents\notes.txt
\d{3}-\d{4}
He said "ready" and left.
Rua das Flores 12
4050-262 Porto
Portugal
first line
second line

逐語的な複数行の文字列には、続きの行のインデントも含め、引用符の間のすべてが含まれます。だから通常は、上のように左端にそろえて書きます。含まれる改行はソースファイルのものなので、Windowsの改行コードで保存されたファイルでは \r\n になります。

C# 11では、両方の問題を解決する生文字列リテラルが追加されました。3つ以上の二重引用符で始まって終わり、エスケープはまったく不要で、閉じ引用符のインデントの分だけインデントを取り除きます。

// C# 11 and later
string json = """
    {
      "name": "Ana",
      "path": "C:\temp"
    }
    """;
// The 4 spaces before the closing """ are removed from every line.

内容自体に """ が含まれる場合は、代わりに4つの引用符で始めて終えます。生文字列リテラルは補間とも組み合わせられます($"""...""")。文字列補間を参照してください。

null、空、空白

文字列の変数は、null(文字列がまったくない)、空(""、長さ0)、または空白だけを含む状態になりえます。2つの静的なヘルパーは、NullReferenceException の危険なしにこれらのケースを確認します。

出力:

null    empty: True   blank: True
""      empty: True   blank: True
"   "   empty: False  blank: True
"Ana"   empty: False  blank: False
True

ユーザーの入力を検証するなら、ほぼ常に string.IsNullOrWhiteSpace が求めているものです。string.Empty と "" は同じ値で、どちらを書くかはスタイルの問題です。

文字のループ

文字列は列挙可能なので、foreach で各 char を訪れます。char 型には文字を分類する静的メソッドがあります。

出力:

letters=6 digits=4 upper=1 symbols=1
!6202tesnuS

文字列の連結

+ は連結し、反対側がどんな型でも使えます(ToString() を呼びます)。多くの値を組み合わせるなら、補間か string.Join を優先します。

出力:

Ana Silva, 31
Ana Silva, 31
AnaSilva
tea | coffee | juice
Total: 23
Total: 5

最後の2行は優先順位の落とし穴を示しています。+ は左から右に評価されるので、左辺が文字列になった時点で、それ以降の + はすべて連結になります。連結の中の算術演算はかっこで囲みます。

よくある質問

C#で文字列の長さを取得するには?

Length プロパティを使います:"hello".Length は5です。メソッドではなくプロパティなので、かっこは付けません。Length はUTF-16のコード単位を数えるので、絵文字など基本多言語面の外にある文字は2と数えられます。null の文字列に対して呼ぶと NullReferenceException が投げられます。

C#で文字列を比較するには?

a == b はテキストを比較し、大文字と小文字を区別します。大文字と小文字を区別しない等価性には string.Equals(a, b, StringComparison.OrdinalIgnoreCase) を使います。文字列を並べ替えたり順序を決めたりするには、string.Compare(a, b, StringComparison.Ordinal) かカルチャを考慮した比較を使い、これは負の数、ゼロ、正の数のいずれかを返します。

C#で複数行の文字列を書くには?

リテラルの前に @ を付けて逐語的にします。複数行にまたがることができ、バックスラッシュはそのままの文字になり、二重引用符は "" と書きます。C# 11以降では、""" で区切る生文字列リテラルも複数行にまたがり、共通のインデントを取り除き、エスケープがまったく不要です。\n や Environment.NewLine で行をつなぐこともできます。

C#の逐語的文字列とは何ですか?

@"C:\Users\ana" のように、前に @ を付けた文字列リテラルです。エスケープシーケンスが処理されないので、バックスラッシュは単なるバックスラッシュであり、Windowsのパスや正規表現に自然に使えます。中で使えるエスケープは、二重引用符を表す "" だけです。

C#のstringとStringの違いは何ですか?

違いはありません。int が System.Int32 の別名であるのと同じく、string は.NETの型 System.String を表すC#のキーワードの別名です。慣例では、宣言には string を、静的な呼び出しには string.IsNullOrEmpty(...) を使いますが、どちらもまったく同じものにコンパイルされます。

C#ではどんなエスケープ文字が使えますか?

\n 改行、\r 復帰、\t タブ、\\ バックスラッシュ、\" 二重引用符、\' 単一引用符、\0 null文字、\uXXXX 16進数のコードによるUTF-16の文字、\U0001F600 8桁のコードによる文字(サロゲートペアになります)、\a 警告音、\b バックスペース、\f 改ページ、\v 垂直タブ、そして \e(C# 13)エスケープです。\d のような未知のシーケンスはコンパイルエラー(CS1009)になるので、正規表現は通常逐語的文字列で書きます。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める