次の方法で共有


文字列と文字列リテラル

文字列は、値がテキストである String 型のオブジェクトです。 内部的には、テキストは、 Char オブジェクトの順次読み取り専用コレクションとして格納されます。 文字列の Length プロパティは、Unicode 文字の数ではなく、文字列に含まれる Char オブジェクトの数を表します。 文字列内の個々の Unicode コード ポイントにアクセスするには、 StringInfo オブジェクトを使用します。

string と System.String

C# では、 string キーワードは Stringのエイリアスであるため、 Stringstring は同等です。 指定されたエイリアス string は、 using System;がなくても動作します。 String クラスには、文字列を安全に作成、操作、および比較するための多くのメソッドが用意されています。 さらに、C# 言語では、一般的な文字列操作を簡略化するために一部の演算子がオーバーロードされます。 キーワードの詳細については、 文字列を参照してください。 型とそのメソッドの詳細については、「 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);

文字列を char 配列で初期化する場合を除き、 新しい 演算子を使用して文字列オブジェクトを作成することはありません。

Empty定数値を使用して文字列を初期化し、文字列の長さが 0 の新しいString オブジェクトを作成します。 長さ 0 の文字列の文字列リテラル表現が ""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

逐語的文字列リテラル

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

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

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"
  ]
}
""";

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

エスケープ シーケンス キャラクター名 Unicode エンコード
\' 単一引用符 0x0027
\" 二重引用符 0x0022
\\ バックスラッシュ 0x005C
\0 無効 0x0000
\a アラート 0x0007
\b バックスペース 0x0008
\e 逃げる 0x001B
\f フォーム フィード 0x000C
\n 新しい行 0x000A
\r キャリッジ リターン 0x000D
\t 水平タブ 0x0009
\v 縦タブ 0x000B
\u Unicode エスケープ シーケンス (UTF-16) \uHHHH (範囲: 0000 - FFFF;例: \u00E7 = "ç")
\U Unicode エスケープ シーケンス (UTF-32) \U00HHHHHH (範囲: 0000000 - 10FFFF;例: \U0001F47D = "👽")
\x 可変長を除く "\u" に似た Unicode エスケープ シーケンス \xH[H][H][H] (範囲: 0 - FFFF;例: \x00E7 または \x0E7 または \xE7 = "ç")

警告

\xエスケープ シーケンスを使用し、4 桁未満の 16 進数を指定する場合、エスケープ シーケンスの直後の文字が有効な 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# チュートリアル を参照してください。

文字列補間を使用して、コードの読みやすさと保守性を向上させます。 文字列補間では、 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# 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.

逐語的な文字列補間

また、C# では、 $@ 構文や @$ 構文を使用して、複数の行にまたがって、逐語的な文字列補間を行うこともできます。

エスケープ シーケンスをリテラルで解釈するには、verbatim 文字列リテラルを使用します。 補間された逐語的な文字列は、 $ 文字の後に @ 文字が続きます。 $トークンと@ トークンは、任意の順序で使用できます。$@"..."@$"..."の両方が有効な補間された逐語的文字列です。

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}.");

// Output:
// Jupiter Hammon
//     was an African American poet born in 1711.
// He was first published in 1761
// at the age of 50.

複合書式指定

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 での複合書式」を参照してください。

部分文字列

部分文字列は、文字列に含まれる任意の文字シーケンスです。 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 文字列と空の文字列

空の文字列は、0 文字を含む 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 オブジェクトを使用して、一連の数値型から文字列を作成します。

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 と文字列」を参照してください。