文字列と文字列リテラル

文字列は、値がテキストの String 型のオブジェクトです。 内部では、テキストは Char オブジェクトの順次読み取り専用コレクションとして格納されます。 C# の文字列の末尾には null 終端文字はありません。したがって、C# の文字列には任意の数の null 文字 ('\0') を埋め込むことができます。 文字列の Length プロパティは、Unicode 文字の数ではなく、文字列に含まれている Char オブジェクトの数を表します。 文字列内の個別の Unicode コード ポイントにアクセスするには、StringInfo オブジェクトを使用します。

文字列と System.String

C# では、string キーワードは String のエイリアスです。 したがって、Stringstring は同等です。ただし、using System; がなくても機能することから、指定されたエイリアス string を使用することを推奨します。 String クラスは、文字列を安全に作成、操作、比較するためのさまざまなメソッドを提供します。 また、C# 言語は、一般的な文字列操作を簡略化するためにいくつかの演算子をオーバーロードします。 キーワードの詳細については、「string」を参照してください。 型およびメソッドの詳細については、「String」を参照してください。

文字列の宣言と初期化

次の例に示すように、文字列はさまざまな方法で宣言および初期化できます。

// Declare without initializing.
string message1;

// Initialize to null.
string message2 = null;

// Initialize as an empty string.
// Use the Empty constant instead of the literal "".
string message3 = System.String.Empty;

// Initialize with a regular string literal.
string oldPath = "c:\\Program Files\\Microsoft Visual Studio 8.0";

// Initialize with a verbatim string literal.
string newPath = @"c:\Program Files\Microsoft Visual Studio 9.0";

// Use System.String if you prefer.
System.String greeting = "Hello World!";

// In local variables (i.e. within a method body)
// you can use implicit typing.
var temp = "I'm still a strongly-typed System.String!";

// Use a const string to prevent 'message4' from
// being used to store another string value.
const string message4 = "You can't get rid of me!";

// Use the String constructor only when creating
// a string from a char*, char[], or sbyte*. See
// System.String documentation for details.
char[] letters = { 'A', 'B', 'C' };
string alphabet = new string(letters);

文字列を文字の配列で初期化する場合を除き、文字列オブジェクトの作成に new 演算子は使用しません。

文字列の長さが 0 の新しい String オブジェクトを作成するには、Empty 定数値で文字列を初期化します。 長さ 0 の文字列のリテラル文字列表現は "" です。 null の代わりに Empty 値を使用して文字列を初期化すると、NullReferenceException が発生する可能性を減らすことができます。 静的な IsNullOrEmpty(String) メソッドを使用すると、アクセスを試行する前に文字列の値を検証できます。

文字列の不変性

文字列オブジェクトは "変更不可" です。つまり、作成した後に変更することはできません。 文字列を変更するように見える String メソッドと C# 演算子はすべて、実際には新しい文字列オブジェクトで結果を返します。 次の例では、s1s2 の内容を連結して 1 つの文字列を形成するときに、2 つの元の文字列は変更されません。 += 演算子で、連結した内容を含む新しい文字列が作成されます。 新しいオブジェクトは変数 s1 に代入され、s1 に代入された元のオブジェクトはガベージ コレクションに対して解放されます。これは、他の変数がこのオブジェクトへの参照を保持していないためです。

string s1 = "A string is more ";
string s2 = "than the sum of its chars.";

// Concatenate s1 and s2. This actually creates a new
// string object and stores it in s1, releasing the
// reference to the original object.
s1 += s2;

System.Console.WriteLine(s1);
// Output: A string is more than the sum of its chars.

文字列の "変更" では、実際には文字列が新しく作成されるため、文字列への参照を作成するときには注意が必要です。 文字列の参照を作成し、元の文字列を "変更" する場合、参照は文字列が変更されたときに作成された新しいオブジェクトではなく、元のオブジェクトを指したままになります。 この動作を表すコードの例を次に示します。

string str1 = "Hello ";
string str2 = str1;
str1 += "World";

System.Console.WriteLine(str2);
//Output: Hello

元の文字列での検索操作や置換操作などの変更に基づく新しい文字列を作成する方法の詳細については、文字列の内容を変更する方法に関する記事をご覧ください。

引用符で囲まれた文字列リテラル

"引用符で囲まれた文字列リテラル" は、同じ行の上で、始まりと終わりに 1 つの二重引用符 (") が置かれます。 引用符で囲まれた文字列リテラルは、1 行に収まり、エスケープ シーケンスを含まない文字列に最適です。 次の例に示すように、引用符で囲まれた文字列リテラルにはエスケープ文字を埋め込む必要があります。

string columns = "Column 1\tColumn 2\tColumn 3";
//Output: Column 1        Column 2        Column 3

string rows = "Row 1\r\nRow 2\r\nRow 3";
/* Output:
    Row 1
    Row 2
    Row 3
*/

string title = "\"The \u00C6olean Harp\", by Samuel Taylor Coleridge";
//Output: "The Æolean Harp", by Samuel Taylor Coleridge

Verbatim 文字列リテラル

"逐語的文字列リテラル" は、複数行の文字列や、円記号文字を含む文字列、二重引用符が埋め込まれた文字列に対してより便利です。 逐語的文字列では、改行文字が文字列テキストの一部として保持されます。 引用符を逐語的文字列に埋め込むには、二重引用符を使用します。 逐語的文字列の一般的な使用方法の例を次に示します。

string filePath = @"C:\Users\scoleridge\Documents\";
//Output: C:\Users\scoleridge\Documents\

string text = @"My pensive SARA ! thy soft cheek reclined
    Thus on mine arm, most soothing sweet it is
    To sit beside our Cot,...";
/* Output:
My pensive SARA ! thy soft cheek reclined
    Thus on mine arm, most soothing sweet it is
    To sit beside our Cot,...
*/

string quote = @"Her name was ""Sara.""";
//Output: Her name was "Sara."

未加工の文字列リテラル

C# 11 以降では、"生文字列リテラル" を使って、複数行の文字列やエスケープ シーケンスが必要な文字を使う文字列をより簡単に作成できます。 "生文字列リテラル" では、エスケープ シーケンスを使用する必要はありません。 空白による書式設定を含む文字列を、出力として表示したいように記述できます。 "生文字列リテラル" は:

  • 始まりと終わりに少なくとも 3 つの二重引用符のシーケンス (""") を置きます。 3 つ (以上) 続く引用符文字を含む文字列リテラルをサポートするために、シーケンスの始まりと終わりに 3 つ以上の文字を続けることができます。
  • 1 行の生文字列リテラルの場合、同じ行に開始と終了の引用符文字を置く必要があります。
  • 複数行の生文字列リテラルの場合、開始と終了の引用符文字を、それぞれ独自の行に置く必要があります。
  • 複数行の生文字列リテラルでは、終了引用符の左側にある空白はすべて削除されます。

次の例は、これらの規則を示しています。

string singleLine = """Friends say "hello" as they pass by.""";
string multiLine = """
    "Hello World!" is typically the first program someone writes.
    """;
string embeddedXML = """
       <element attr = "content">
           <body style="normal">
               Here is the main text
           </body>
           <footer>
               Excerpts from "An amazing story"
           </footer>
       </element >
       """;
// The line "<element attr = "content">" starts in the first column.
// All whitespace left of that column is removed from the string.

string rawStringLiteralDelimiter = """"
    Raw string literals are delimited 
    by a string of at least three double quotes,
    like this: """
    """";

次の例は、これらの規則に基づいて報告されるコンパイラ エラーを示しています。

// CS8997: Unterminated raw string literal.
var multiLineStart = """This
    is the beginning of a string 
    """;

// CS9000: Raw string literal delimiter must be on its own line.
var multiLineEnd = """
    This is the beginning of a string """;

// CS8999: Line does not start with the same whitespace as the closing line
// of the raw string literal
var noOutdenting = """
    A line of text.
Trying to outdent the second line.
    """;

最初の 2 つの例が無効であるのは、複数行の生文字列リテラルでは独自の行に開始と終了の引用符シーケンスを置く必要があるためです。 3 番目の例が無効であるのは、テキストが終了引用符シーケンスからインデント解除されているためです。

引用符で囲まれた文字列リテラルか逐語的文字列リテラルを使うときに、エスケープ シーケンスが必要な文字を含むテキストを生成する場合は、生文字列リテラルを検討すべきです。 生文字列リテラルは、出力テキストにさらに似たものになるため、作成者にとっても他のユーザーにとっても読みやすくなります。 たとえば、書式設定された JSON の文字列を含む次のコードを考えてみましょう。

string jsonString = """
{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25,
  "Summary": "Hot",
  "DatesAvailable": [
    "2019-08-01T00:00:00-07:00",
    "2019-08-02T00:00:00-07:00"
  ],
  "TemperatureRanges": {
    "Cold": {
      "High": 20,
      "Low": -10
    },
    "Hot": {
      "High": 60,
      "Low": 20
    }
            },
  "SummaryWords": [
    "Cool",
    "Windy",
    "Humid"
  ]
}
""";

このテキストを、JSON シリアル化に関するサンプル内の、同等のテキストと比較してみてください。そこではこの新機能が使われていません。

文字列のエスケープ シーケンス

エスケープ シーケンス 文字名 Unicode エンコーディング
\' 単一引用符 0x0027
\" 二重引用符 0x0022
\\ 円記号 0x005C
\0 Null 0x0000
\a 警告 0x0007
\b バックスペース 0x0008
\f フォーム フィード 0x000C
\n 改行 0x000A
\r キャリッジ リターン 0x000D
\t 水平タブ 0x0009
\v 垂直タブ 0x000B
\u Unicode エスケープ シーケンス (UTF-16) \uHHHH (範囲:0000 - FFFF; 例: \u00E7 = "ç")
\U Unicode エスケープ シーケンス (UTF-32) \U00HHHHHH (範囲: 000000 - 10FFFF; 例: \U0001F47D = "👽")
\x 可変長である点を除き "\u" に類似した Unicode エスケープ シーケンス \xH[H][H][H] (範囲:0 - FFFF; 例: \x00E7\x0E7、または \xE7 = "ç")

警告

\x のエスケープ シーケンスを使用していて、指定している 16 進数が 4 桁未満である場合に、エスケープ シーケンスの直後の文字が有効な 16 進数 (0-9、A-F、a-f) であると、それらはエスケープ シーケンスの一部として解釈されます。 たとえば、\xA1 はコード ポイント U+00A1 の "¡" を生成します。 ただし、次の文字が "A" または "a" である場合、エスケープ シーケンスは代わりに \xA1A であると解釈され、コード ポイント U+0A1A の "ਚ" を生成します。 そのような場合、4 桁の 16 進数すべてを指定する (例: \x00A1) と、誤って解釈される可能性がすべて排除されます。

注意

コンパイル時に、逐語的文字列はエスケープ シーケンスと同様に通常の文字列に変換されます。 したがって、逐語的文字列をデバッガーのウォッチ ウィンドウで表示すると、ソース コードの逐語的バージョンではなく、コンパイラが追加したエスケープ文字が表示されます。 たとえば、逐語的文字列 @"C:\files.txt" は、ウォッチ ウィンドウでは "C:\\files.txt" と表示されます。

書式指定文字列

書式指定文字列は、その内容が実行時に動的に決定される文字列です。 書式指定文字列を作成するには、文字列内の中かっこの内側に "挿入式" かプレースホルダーを埋め込みます。 中かっこ ({...}) 内にあるものはすべて値に解決され、実行時に書式設定された文字列として出力されます。 書式指定文字列を作成するには、文字列補間と複合書式設定の 2 つの方法があります。

文字列補間

C# 6.0 以降で使用できる "補間文字列" は、特殊文字 $ によって識別され、中かっこ内に挿入式を含みます。 文字列補間を初めて使用する場合は、簡単な概要として「文字列補間 - C# の対話形式チュートリアル」をご覧ください。

文字列補間を使ってコードの読みやすさと保守性を向上させます。 文字列補間がもたらす結果は String.Format メソッドと同じですが、使いやすさとインラインのわかりやすさが向上します。

var jh = (firstName: "Jupiter", lastName: "Hammon", born: 1711, published: 1761);
Console.WriteLine($"{jh.firstName} {jh.lastName} was an African American poet born in {jh.born}.");
Console.WriteLine($"He was first published in {jh.published} at the age of {jh.published - jh.born}.");
Console.WriteLine($"He'd be over {Math.Round((2018d - jh.born) / 100d) * 100d} years old today.");

// Output:
// Jupiter Hammon was an African American poet born in 1711.
// He was first published in 1761 at the age of 50.
// He'd be over 300 years old today.

C# 10 以降では、プレースホルダーに使用されるすべての式が定数文字列である場合も、文字列補間を使用して定数文字列を初期化できます。

C# 11 以降では、"生文字列リテラル" を文字列補間と組み合わせることができます。 書式指定文字列は、3 つ以上の連続する二重引用符で開始および終了します。 出力文字列に { または } 文字を含める必要がある場合は、追加の $ 文字を使って、補間を開始および終了する {} の文字数を指定できます。 それより数が少ない { または } 文字のシーケンスは、出力に含まれます。 次の例は、その機能を使って、点の原点からの距離を表示し、その点を中かっこ内に置く方法を示しています。

int X = 2;
int Y = 3;

var pointMessage = $$"""The point {{{X}}, {{Y}}} is {{Math.Sqrt(X * X + Y * Y)}} from the origin.""";

Console.WriteLine(pointMessage);
// Output:
// The point {2, 3} is 3.605551275463989 from the origin.

複合書式指定

String.Format では、中かっこ内のプレースホルダーを使って書式指定文字列を作成します。 この例では、上で使用した文字列補間の方法と同様の出力が生じます。

var pw = (firstName: "Phillis", lastName: "Wheatley", born: 1753, published: 1773);
Console.WriteLine("{0} {1} was an African American poet born in {2}.", pw.firstName, pw.lastName, pw.born);
Console.WriteLine("She was first published in {0} at the age of {1}.", pw.published, pw.published - pw.born);
Console.WriteLine("She'd be over {0} years old today.", Math.Round((2018d - pw.born) / 100d) * 100d);

// Output:
// Phillis Wheatley was an African American poet born in 1753.
// She was first published in 1773 at the age of 20.
// She'd be over 300 years old today.

.NET 型の書式設定について詳しくは、「.NET での型の書式設定」をご覧ください。

部分文字列

部分文字列は、1 つの文字列に含まれる一連の文字です。 元の文字列の一部から新しい文字列を作成するには、Substring メソッドを使用します。 IndexOf メソッドを使用して、1 つまたは複数の部分文字列を検索できます。 指定されたすべての部分文字列を新しい文字列に置換するには、Replace メソッドを使用します。 Substring メソッドと同様に、Replace は実際に新しい文字列を返し、元の文字列は変更しません。 詳細については、「文字列を検索する方法」と文字列の内容を変更する方法に関する記事をご覧ください。

string s3 = "Visual C# Express";
System.Console.WriteLine(s3.Substring(7, 2));
// Output: "C#"

System.Console.WriteLine(s3.Replace("C#", "Basic"));
// Output: "Visual Basic Express"

// Index values are zero-based
int index = s3.IndexOf("C");
// index = 7

各文字へのアクセス

次の例に示すように、配列表記とインデックス値を使用すると、それぞれの文字への読み取り専用アクセスが可能になります。

string s5 = "Printing backwards";

for (int i = 0; i < s5.Length; i++)
{
    System.Console.Write(s5[s5.Length - i - 1]);
}
// Output: "sdrawkcab gnitnirP"

String メソッドが、文字列内の個別の文字を変更する必要がある機能を提供していない場合は、StringBuilder オブジェクトを使用して個別の文字の "埋め込み先" を変更し、StringBuilder メソッドを使用することで、結果を格納する新しい文字列を作成できます。 次の例では、特定の方法で元の文字列を変更し、将来使用するためにその結果を保存する必要があるとします。

string question = "hOW DOES mICROSOFT wORD DEAL WITH THE cAPS lOCK KEY?";
System.Text.StringBuilder sb = new System.Text.StringBuilder(question);

for (int j = 0; j < sb.Length; j++)
{
    if (System.Char.IsLower(sb[j]) == true)
        sb[j] = System.Char.ToUpper(sb[j]);
    else if (System.Char.IsUpper(sb[j]) == true)
        sb[j] = System.Char.ToLower(sb[j]);
}
// Store the new string.
string corrected = sb.ToString();
System.Console.WriteLine(corrected);
// Output: How does Microsoft Word deal with the Caps Lock key?

null 文字列と空の文字列

空の文字列はゼロ文字を含む System.String オブジェクトのインスタンスです。 空の文字列は、空のテキスト フィールドを表すため、さまざまなプログラミング シナリオでよく使用されます。 空の文字列でメソッドを呼び出すことができます。それは有効な System.String オブジェクトであるためです。 空の文字列は、次のように初期化されます。

string s = String.Empty;

これに対し、null 文字列は System.String オブジェクトのインスタンスを参照しないので、null 文字列でメソッドを呼び出そうとすると NullReferenceException が発生します。 しかし、null 文字列を他の文字列に連結したり、他の文字列と比較することは可能です。 次に、null 文字列の参照によって例外がスローされる場合とされない場合の例を示します。

string str = "hello";
string nullStr = null;
string emptyStr = String.Empty;

string tempStr = str + nullStr;
// Output of the following line: hello
Console.WriteLine(tempStr);

bool b = (emptyStr == nullStr);
// Output of the following line: False
Console.WriteLine(b);

// The following line creates a new empty string.
string newStr = emptyStr + nullStr;

// Null strings and empty strings behave differently. The following
// two lines display 0.
Console.WriteLine(emptyStr.Length);
Console.WriteLine(newStr.Length);
// The following line raises a NullReferenceException.
//Console.WriteLine(nullStr.Length);

// The null character can be displayed and counted, like other chars.
string s1 = "\x0" + "abc";
string s2 = "abc" + "\x0";
// Output of the following line: * abc*
Console.WriteLine("*" + s1 + "*");
// Output of the following line: *abc *
Console.WriteLine("*" + s2 + "*");
// Output of the following line: 4
Console.WriteLine(s2.Length);

文字列を迅速に作成するための StringBuilder の使用

.NET での文字列操作は高度に最適化されており、ほとんどの場合パフォーマンスに大きく影響することはありません。 ただし、短いループが数百回または数千回実行されている場合など、シナリオによっては文字列操作がパフォーマンスに影響する可能性があります。 StringBuilder クラスが作成する文字列バッファーにより、プログラムで大量の文字列操作を実行する場合のパフォーマンスが向上します。 また、StringBuilder 文字列を使用すると、組み込み文字列データ型ではサポートされていない個別の文字を再割り当てできます。 たとえば、このコードでは、新しい文字列を作成せずに、文字列の内容を変更します。

System.Text.StringBuilder sb = new System.Text.StringBuilder("Rat: the ideal pet");
sb[0] = 'C';
System.Console.WriteLine(sb.ToString());
//Outputs Cat: the ideal pet

この例では、StringBuilder オブジェクトを使用して、複数の数値型から 1 つの文字列を作成します。

var sb = new StringBuilder();

// Create a string composed of numbers 0 - 9
for (int i = 0; i < 10; i++)
{
    sb.Append(i.ToString());
}
Console.WriteLine(sb);  // displays 0123456789

// Copy one character of the string (not possible with a System.String)
sb[0] = sb[9];

Console.WriteLine(sb);  // displays 9123456789

文字列、拡張メソッド、LINQ

String 型は、IEnumerable<T> を実装するので、文字列には Enumerable クラスで定義した拡張メソッドを使用できます。 見やすさを考慮して、これらのメソッドは String 型の IntelliSense からは除外されていますが、使用できます。 文字列で LINQ クエリ式を使用することもできます。 詳細については、「LINQ と文字列」を参照してください。