문자열 및 문자열 리터럴

문자열은 값이 텍스트인 String 형식의 개체입니다. 내부적으로 텍스트는 Char 개체의 순차적 읽기 전용 컬렉션으로 저장됩니다. C# 문자열의 끝에 null 종료 문자가 없습니다. 따라서 C# 문자열에는 임의 개수의 포함된 null 문자('\0')가 포함될 수 있습니다. 문자열의 Length 속성은 유니코드 문자 수가 아닌 포함된 Char 개체 수를 나타냅니다. 문자열에서 개별 유니코드 코드 포인트에 액세스하려면 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);

chars 배열을 사용하여 문자열을 초기화하는 경우를 제외하고 연산자를 사용하여 문자열 개체를 만들지 않습니다.

문자열 길이가 0인 새 String 개체를 만들려면 Empty 상수 값이 포함된 문자열을 초기화하세요. 빈 문자열을 문자열 리터럴로 나타내면 ""로 표시됩니다. null 대신 Empty 값이 포함된 문자열을 초기화하면 NullReferenceException 발생을 줄일 수 있습니다. 액세스하기 전에 문자열의 값을 확인하려면 정적 IsNullOrEmpty(String) 메서드를 사용하세요.

문자열의 불변성

문자열 개체는 변경할 수 없습니다. 문자열 개체를 만든 후에는 변경할 수 없습니다. 실제로 문자열을 수정하는 것으로 나타나는 모든 String 메서드 및 C# 연산자는 새로운 문자열 개체에 결과를 반환합니다. 다음 예제에서 s1s2의 콘텐츠는 단일 문자열을 형성하도록 연결되며, 두 개의 원본 문자열은 변경되지 않습니다. += 연산자는 결합된 콘텐츠를 포함하는 새 문자열을 만듭니다. 새 개체는 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

원래 문자열에 대한 검색 및 바꾸기 작업과 같이, 수정을 기반으로 하는 새 문자열 작성 방법에 대한 자세한 내용은 문자열 콘텐츠 수정 방법을 참조하세요.

따옴표 붙은 문자열 리터럴

따옴표 붙은 문자열 리터럴 은 동일한 줄에 작은따옴표 문자(")로 시작되고 끝납니다. 따옴표 붙은 문자열 리터럴은 한 줄에 맞고 이 스케이프 시퀀스를 포함하지 않는 문자열에 가장 적합합니다. 따옴표 붙은 문자열 리터럴은 다음 예제와 같이 이스케이프 문자를 포함해야 합니다.

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 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부터 원시 문자열 리터럴을 사용하여 여러 줄인 문자열을 더 쉽게 만들거나 이스케이프 시퀀스가 필요한 문자를 사용할 수 있습니다. 원시 문자열 리터럴은 이스케이프 시퀀스를 사용할 필요가 없습니다. 공백 서식을 포함하여 문자열을 출력에 표시할 방법을 작성할 수 있습니다. 원시 문자열 리터럴:

  • 세 개 이상의 큰따옴표(""") 시퀀스로 시작하고 끝납니다. 세 개 이상의 반복된 따옴표 문자가 포함된 문자열 리터럴을 지원하기 위해 시퀀스를 시작하고 종료할 수 있습니다.
  • 한 줄 원시 문자열 리터럴에는 같은 줄에 여는 따옴표 문자와 닫는 따옴표 문자가 필요합니다.
  • 여러 줄 원시 문자열 리터럴에는 자체 줄에 여는 따옴표 문자와 닫는 따옴표 문자가 모두 필요합니다.
  • 여러 줄 원시 문자열 리터럴에서는 닫는 따옴표 왼쪽의 공백이 모두 제거됩니다.

다음 예제에서는 이러한 규칙을 보여 줍니다.

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

여러 줄 원시 문자열 리터럴에는 자체 줄에 여는 따옴표 시퀀스와 닫는 따옴표 시퀀스가 필요하기 때문에 처음 두 예제는 유효하지 않습니다. 세 번째 예제는 텍스트가 닫는 따옴표 시퀀스에서 들여쓰기되어 있기 때문에 유효하지 않습니다.

따옴표 붙은 문자열 리터럴 또는 축자 문자열 리터럴을 사용할 때 이 스케이프 시퀀스가 필요한 문자를 포함하는 텍스트를 생성할 때 원시 문자열 리터럴을 고려해야 합니다. 원시 문자열 리터럴은 출력 텍스트와 더 유사하므로 사용자와 다른 사용자가 더 쉽게 읽을 수 있습니다. 예를 들어 형식이 지정된 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 serialization 샘플의 해당 텍스트와 해당 텍스트를 비교합니다.

문자열 이스케이프 시퀀스

이스케이프 시퀀스 문자 이름 유니코드 인코딩
\' 작은따옴표 0x0027
\" 큰따옴표 0x0022
\\ 백슬래시 0x005C
\0 Null 0x0000
\a 경고 0x0007
\b 백스페이스 0x0008
\f 폼 피드 0x000C
\n 줄 바꿈 0x000A
\r 캐리지 리턴 0x000D
\t 가로 탭 0x0009
\v 세로 탭 0x000B
\u 유니코드 이스케이프 시퀀스(UTF-16) \uHHHH (범위: 0000-FFFF; 예제: \u00E7 “ç” =)
\U 유니코드 이스케이프 시퀀스(UTF-32) \U00HHHHHH (range: 000000 - 10FFFF; example: \U0001F47D = "👽")
\x 길이가 변하는 경우를 제외하고 “\u”와 유사한 유니코드 이스케이프 시퀀스합니다. \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"로 표시됩니다.

형식 문자열

형식 문자열은 콘텐츠가 런타임에 동적으로 결정되는 문자열입니다. 형식 문자열은 문자열 내의 중괄호 안에 ‘보간된 식’이나 자리 표시자를 포함하여 만들어집니다. 중괄호({...}) 안의 모든 내용은 런타임에 하나의 값으로 확인되고 형식화된 문자열로 출력됩니다. 형식 문자열을 만드는 두 가지 방법은 문자열 보간 및 복합 형식 지정입니다.

문자열 보간

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부터 원시 문자열 리터럴을 문자열 보간과 결합할 수 있습니다. 세 개 이상의 연속 큰따옴표로 서식 문자열을 시작하고 종료합니다. 출력 문자열에 또는 문자가 { 포함되어야 하는 경우 추가 $ 문자를 사용하여 보간을 시작하고 종료하는 문자 수를 } 지정할 {} 있습니다. 출력에 더 적 { 거나 } 문자의 모든 시퀀스가 포함됩니다. 다음 예제에서는 해당 기능을 사용하여 원점에서 점의 거리를 표시하고 중괄호 안에 점을 배치하는 방법을 보여 줍니다.

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의 형식 서식 지정을 참조하세요.

부분 문자열

부분 문자열은 문자열에 포함된 임의의 문자 시퀀스입니다. 원래 문자열 일부에서 새 문자열을 만들려면 Substring 메서드를 사용하세요. IndexOf 메서드를 사용하면 부분 문자열 항목을 하나 이상 검색할 수 있습니다. 지정된 부분 문자열의 모든 항목을 새 문자열로 바꾸려면 Replace 메서드를 사용하세요. 메서드 ReplaceSubstring 마찬가지로 는 실제로 새 문자열을 반환하고 원래 문자열을 수정하지 않습니다. 자세한 내용은 문자열 검색 방법문자열 내용 수정 방법을 참조하세요.

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 문자열에 대한 참조로 인해 예외가 throw되지 않는 몇 가지 사례를 보여 줍니다.

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 및 문자열을 참조하세요.