C#의 string은 UTF-16 문자(char 값)의 시퀀스입니다. 참조 형식이지만 중요한 면에서는 값처럼 동작합니다. 불변이고, ==는 객체의 동일성이 아니라 텍스트를 비교합니다. string은 .NET 타입 System.String의 C# 키워드이며, 둘은 서로 바꿔 쓸 수 있습니다.
출력:
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 코드 단위를 셉니다. 대부분 언어의 대부분 글자는 한 단위이지만, 이모지와 일부 드문 문자는 두 단위(서로게이트 쌍)를 쓰므로 "👍".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()로 비교하는 것은 피하세요. 새 문자열을 두 개 할당하고, 일부 문화권에서 틀린 답을 냅니다(터키어의 점 있는 i와 점 없는 i가 대표적인 사례입니다).
a가 null일 때 a.Equals(b)를 호출하면 NullReferenceException이 발생하지만, a == b와 정적 메서드 string.Equals(a, b)는 null을 안전하게 처리합니다.
이스케이프 문자
일반 문자열 리터럴 안에서 백슬래시는 이스케이프 시퀀스를 시작합니다:
| 이스케이프 | 의미 |
|---|---|
\n | 새 줄(라인 피드) |
\r | 캐리지 리턴 |
\t | 탭 |
\\ | 백슬래시 |
\" | 큰따옴표 |
\' | 작은따옴표(char 리터럴에서만 필요) |
\0 | 널 문자 |
\uXXXX | 4자리 16진 코드로 지정하는 UTF-16 문자, 예: \u00e9 |
\U0001F44D | 8자리 16진 코드로 지정하는 문자. U+FFFF보다 크면 char 값 두 개가 됩니다 |
\xH부터 \xHHHH | 1~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, unrecognized escape sequence)입니다. Windows 경로나 정규식을 일반 문자열로 쓰면 이 오류가 납니다.
축자 문자열과 여러 줄 문자열
리터럴 앞에 @를 붙이면 축자 문자열이 됩니다. 백슬래시는 그대로의 문자이고, 문자열이 여러 줄에 걸칠 수 있습니다. 남은 유일한 이스케이프는 큰따옴표 하나를 만드는 ""입니다.
출력:
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은 두 문제를 모두 해결하는 원시 문자열 리터럴을 추가했습니다. 세 개 이상의 큰따옴표로 시작하고 끝나며, 이스케이프가 전혀 필요 없고, 닫는 따옴표가 들여쓰기된 만큼의 들여쓰기를 제거합니다:
// C# 11 and later
string json = """
{
"name": "Ana",
"path": "C:\temp"
}
""";
// The 4 spaces before the closing """ are removed from every line.
내용 자체에 """가 있으면 네 개의 따옴표로 시작하고 끝내세요. 원시 리터럴은 보간과도 함께 쓸 수 있습니다($"""..."""). 문자열 보간을 참고하세요.
null, 빈 문자열, 공백
문자열 변수는 null(문자열이 아예 없음)이거나, 비어 있거나("", 길이 0), 공백만 담고 있을 수 있습니다. 두 정적 헬퍼가 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
마지막 두 줄은 우선순위 함정을 보여 줍니다. +는 왼쪽부터 평가되므로, 왼쪽이 한 번 문자열이 되면 이후의 모든 +는 이어 붙이기입니다. 이어 붙이기 안의 산술 연산은 괄호로 감싸세요.
자주 묻는 질문
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)이나 문화권을 고려한 비교를 쓰며, 이들은 음수, 0, 양수를 반환합니다.
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 널 문자, \uXXXX 16진 코드로 지정하는 UTF-16 문자, \U0001F600 8자리 코드로 지정하는 문자(서로게이트 쌍이 됩니다), \a 경고음, \b 백스페이스, \f 폼 피드, \v 수직 탭, \e(C# 13) 이스케이프입니다. \d 같은 알 수 없는 시퀀스는 컴파일 오류(CS1009)이므로, 정규식은 보통 축자 문자열로 씁니다.