Menu

C# Substring: 문자열 일부를 안전하게 자르기

C#에서 String.Substring이 동작하는 방식: 시작 인덱스와 길이 인수, ArgumentOutOfRangeException과 이를 피하는 방법, IndexOf 결과 주변의 텍스트 추출, 앞이나 뒤의 n글자 가져오기, C# 8의 범위 연산자를 알아봅니다.

이 페이지에는 실행 가능한 에디터가 있습니다 - 편집하고 실행하면 결과를 바로 볼 수 있습니다.

Substring은 기존 문자열의 일부로 이루어진 새 문자열을 반환합니다. 오버로드가 두 개 있습니다:

  • s.Substring(startIndex): startIndex부터 끝까지.
  • s.Substring(startIndex, length): startIndex에서 시작하는 length개의 문자.

인덱스는 0부터 시작하고, 두 번째 인수는 끝 위치가 아니라 길이입니다. 이 점이 JavaScript의 substring(start, end), Java의 substring(begin, end)와 다르며, 코드를 옮길 때 생기는 많은 인덱스 어긋남 버그의 원인입니다.

출력:

4271-BLK
SHOE
4271
BLK
0
SHOE-4271-BLK

다른 모든 문자열 메서드처럼 Substring도 원본을 수정하지 않고 새 문자열을 반환하므로 결과를 대입하세요.

ArgumentOutOfRangeException과 피하는 방법

요청한 범위가 문자열 안에 들어가지 않으면 Substring은 ArgumentOutOfRangeException을 던집니다. 음수 startIndex, Length보다 큰 startIndex, 음수 length, 또는 Length보다 큰 startIndex + length인 경우입니다. Python 슬라이스와 달리 조용히 잘라 주지 않습니다.

출력:

Substring(2, 4) -> "ffee"
Substring(2, 5) -> ArgumentOutOfRangeException
Substring(6) -> ""
Substring(7) -> ArgumentOutOfRangeException
Substring(-1) -> ArgumentOutOfRangeException
Substring(0, 10) -> ArgumentOutOfRangeException

보통은 Math.Min으로 길이를 문자열의 남은 부분으로 제한해 막습니다:

출력:

ffee
ea
[]
[]

null 문자열에 Substring을 호출하면 ArgumentOutOfRangeException이 아니라 NullReferenceException이 발생하므로, 헬퍼가 null부터 확인합니다.

앞과 뒤의 n글자

접두사나 접미사를 가져오는 것이 Substring의 가장 흔한 용도입니다. 둘 다 문자열이 n보다 짧으면 예외를 던지므로, 실제 코드에서는 길이를 확인합니다:

출력:

**** 1111
An Unexpecte...
Hi
Hi

문자열의 앞부분을 버리고 싶을 때는 Substring(n)만으로 충분합니다. "#FF8800".Substring(1)은 "FF8800"입니다. 관련 메서드 Remove(start, count)는 Substring의 반대로, 그 범위를 빼낸 문자열을 반환합니다.

IndexOf와 함께 쓰는 Substring

실제 부분 문자열은 대부분 세는 것이 아니라 찾아냅니다. IndexOf는 문자나 문자열의 위치를 반환하고(없으면 -1), LastIndexOf는 끝에서부터 찾습니다. Substring과 조합해 구분자 주변의 텍스트를 잘라 냅니다:

출력:

maya.lopez
example.com
pdf
report.final.v2
WARN
Disk almost full

계산은 한 가지 규칙을 따릅니다. 위치 a와 b 사이의 텍스트(둘 다 제외)를 가져오려면 시작은 a + 1, 길이는 b - a - 1입니다.

위험한 것은 IndexOf가 -1을 반환하는 경우입니다. email.Substring(0, -1)은 예외를 던지고, 더 나쁘게도 email.Substring(-1 + 1)은 조용히 문자열 전체를 반환합니다. 항상 확인하세요:

출력:

shop.io
(none)

대안으로서의 Split

문자열이 구분자로 나뉜 필드 목록이라면, IndexOf와 Substring을 연달아 호출하는 것보다 Split이 간단하고, 틀릴 인덱스 계산도 없습니다:

출력:

Ana Silva
Porto
maya.lopez
beach.jpg

Split은 배열 하나와 필드마다 문자열 하나를 할당하므로, 성능이 중요한 루프에서 한 번만 자를 때는 IndexOf와 Substring이 더 저렴합니다. 그 밖의 경우에는 가독성이 우선입니다. 더 많은 옵션(여러 구분자, 빈 항목 제거, 최대 개수)은 문자열 메서드에 있습니다. "ID: 뒤의 숫자" 같은 패턴에는 보통 정규식이 가장 명확한 도구입니다.

범위 연산자(C# 8 이상)

C# 8은 문자열과 배열에 쓸 수 있는 인덱스와 범위 문법을 추가했습니다. ^n은 "끝에서 n번째"를 뜻하고, a..b는 a부터 b 직전까지의(b는 포함하지 않음) 범위이므로, 두 번째 숫자는 길이가 아니라 끝 인덱스입니다:

// C# 8 and later
string sku = "SHOE-4271-BLK";

string first4 = sku[..4];      // "SHOE"        same as Substring(0, 4)
string middle = sku[5..9];     // "4271"        same as Substring(5, 4)
string last3  = sku[^3..];     // "BLK"         same as Substring(sku.Length - 3)
string noEnds = sku[1..^1];    // "HOE-4271-BL" drop first and last
char lastChar = sku[^1];       // 'K'

문자열에서 범위는 Substring 호출로 컴파일되므로 새 문자열을 할당하고, 같은 범위 초과 상황에서 ArgumentOutOfRangeException을 던집니다. 어느 쪽을 쓸지는 스타일 문제입니다. 양 끝을 서로 다른 쪽에서 잴 때([1..^1])는 범위가 더 읽기 좋습니다.

복사 없는 부분 문자열: Span

Substring을 호출할 때마다 새 문자열이 할당됩니다. 성능이 중요한 파싱 코드(큰 파일 읽기, 많은 요청 처리)를 위해 .NET Core 2.1 이상은 원본 문자열의 일부를 복사 없이 보여 주는 ReadOnlySpan<char>를 제공합니다:

ReadOnlySpan<char> line = "2026-09-24,42.50,EUR".AsSpan();
ReadOnlySpan<char> amount = line.Slice(11, 5);          // "42.50", no allocation
decimal value = decimal.Parse(amount, provider: CultureInfo.InvariantCulture);

Span은 일반 클래스의 필드에 저장할 수 없고 await를 넘어서 쓸 수 없으므로, 짧고 동기적인 코드에 어울립니다. 일반적인 애플리케이션 코드에는 Substring이 맞는 도구입니다.

흔한 실수

  • 끝 인덱스를 길이로 넘기기. s.Substring(2, 5)는 "인덱스 2부터 5까지"가 아니라 인덱스 2부터 다섯 글자입니다. 인덱스 a부터 b 직전까지의 텍스트를 가져오려면 길이는 b - a입니다.
  • IndexOf의 -1을 확인하지 않기. 예외가 나거나, + 1을 붙였다면 실수로 문자열 전체를 얻게 됩니다.
  • 짧은 문자열도 충분히 길다고 가정하기. 이름, 코드, 파일의 한 줄은 예상보다 짧을 수 있습니다. Math.Min으로 제한하거나 Length를 확인하세요.
  • 결과 버리기. 한 줄에 단독으로 쓴 s.Substring(1);은 아무 쓸모 있는 일도 하지 않습니다.

자주 묻는 질문

C#에서 Substring은 어떻게 동작하나요?

s.Substring(start)는 인덱스 start부터 끝까지의 문자를 반환하고, s.Substring(start, length)는 start에서 시작하는 length개의 문자를 반환합니다. 인덱스는 0부터 시작하고, 두 번째 인수는 끝 인덱스가 아니라 길이입니다: "Hello".Substring(1, 3)은 "ell"입니다. 원본 문자열은 바뀌지 않습니다.

Substring이 ArgumentOutOfRangeException을 던지는 이유는 무엇인가요?

start가 음수이거나 문자열 길이보다 크거나, start + length가 끝을 넘기 때문입니다. 가장 흔한 원인은 더 긴 문자열을 기준으로 계산한 길이, 또는 -1인 IndexOf 결과를 시작 위치로 쓴 경우입니다. Math.Min(length, s.Length - start)로 길이를 제한하고, IndexOf 결과는 쓰기 전에 확인하세요.

C#에서 문자열의 마지막 n글자는 어떻게 가져오나요?

n <= s.Length라면 s.Substring(s.Length - n)이 마지막 n글자를 반환합니다. 짧은 문자열에도 안전하려면 s.Length <= n ? s : s.Substring(s.Length - n)을 쓰세요. C# 8 이상에서는 s[^n..]이라고 쓸 수도 있는데, n이 너무 크면 똑같이 예외를 던집니다.

C#에서 두 문자 사이의 문자열은 어떻게 가져오나요?

IndexOf로 두 위치를 찾은 뒤 그 사이를 가져옵니다: int start = s.IndexOf('(') + 1; int end = s.IndexOf(')', start); string inside = s.Substring(start, end - start);. Substring을 호출하기 전에 각 IndexOf가 무언가를 찾았는지(-1이 아닌지) 확인하세요. 복잡한 패턴에는 정규식이 더 명확합니다.

C#의 Substring은 0부터 시작하나요?

네. 첫 문자는 인덱스 0, 마지막 문자는 Length - 1에 있습니다. Substring(0, 3)은 처음 세 글자를 반환합니다. Length 자체를 시작 위치로 넘기는 것은 허용되며 빈 문자열을 반환합니다.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기