Menu

C# 정규식(Regex): Match, Replace, Split, 그룹 예제

System.Text.RegularExpressions로 C#에서 정규식을 쓰는 방법: IsMatch, Match와 Matches, 번호 그룹과 이름 있는 그룹, 치환 문자열과 람다를 쓰는 Replace, Split, RegexOptions, 축자 패턴 문자열, 입력 검증, 시간 제한, 그리고 .NET 7 GeneratedRegex 특성을 알아봅니다.

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

정규식(regex)은 텍스트를 설명하는 패턴입니다. "숫자 네 개, 대시, 숫자 두 개", "대문자로 시작하는 단어", "대괄호 사이의 모든 것" 같은 것입니다. C#에서는 System.Text.RegularExpressions의 Regex 클래스가 이런 패턴으로 텍스트를 찾고, 추출하고, 바꾸고, 나눕니다.

IsMatch, Match, Matches

세 메서드가 대부분의 검색을 처리합니다. IsMatch는 예 또는 아니요로 답하고, Match는 첫 일치 항목을, Matches는 모든 일치 항목을 반환합니다:

출력:

True
2026-03-14 at index 22
4 matches:
  1042
  2026
  1043
  2026
Success: False, Value: []

실패한 Match는 null을 반환하지 않습니다. Success가 false이고 Value가 비어 있는 Match를 반환합니다. 일치 항목을 쓰기 전에 항상 Success를 검사하세요.

네 자리 숫자 검색은 패턴이 의도보다 많이 잡는 모습을 보여 줍니다. \b\d{4}\b는 "하나의 단어로서 정확히 네 자리 숫자"라는 뜻이고, -가 단어 경계이므로 날짜 안의 연도도 해당합니다. 예를 들어 (?<=Order |order )\d+처럼 뜻하는 바를 말하도록 패턴을 좁히거나, 다음에 나오는 그룹을 쓰세요.

패턴을 위한 축자 문자열

정규식 문법은 곳곳에서 백슬래시를 쓰고(\d, \s, \b), C# 문자열 이스케이프도 그렇습니다. 일반 문자열에서 "\d"는 컴파일조차 되지 않고(CS1009, 인식할 수 없는 이스케이프 시퀀스), "\b"는 백스페이스 문자로 컴파일되어 패턴을 조용히 바꿉니다. 백슬래시가 그냥 백슬래시인 @를 붙인 축자 문자열로 패턴을 쓰세요:

var a = new Regex("\\d+\\.\\d{2}");   // escaped twice: hard to read
var b = new Regex(@"\d+\.\d{2}");     // verbatim: what the regex engine sees

축자 문자열에서 큰따옴표는 ""로 씁니다. C# 11의 원시 문자열 리터럴("""...""")은 그것조차 피합니다.

그룹: 일치 항목의 일부 추출하기

괄호는 그룹을 만들고, 일치 후 각 그룹의 텍스트를 쓸 수 있습니다. 그룹은 여는 괄호 순서대로 1부터 번호가 매겨지고, 그룹 0은 일치 항목 전체입니다. 이름 있는 그룹 (?<name>...)은 읽기 쉽고 패턴을 고쳐도 살아남습니다:

출력:

2026-03-14
ERROR
payments
Card declined for order 1042
60 by 90, area 5400

정규식에서 [와 ]는 의미(문자 클래스)가 있으므로, 대괄호 문자 자체를 찾으려면 \[와 \]가 필요합니다. . * + ? ( ) { } ^ $ | \도 마찬가지입니다. 사용자가 준 문자열을 패턴 안에서 그대로 찾으려면 메타 문자를 이스케이프해 주는 Regex.Escape를 거치세요: Regex.Escape("price (USD)")는 price\ \(USD\)를 반환합니다(공백도 이스케이프되지만 해롭지 않습니다).

캡처하지 않고 묶기만 하는 괄호는 (?:...)로 씁니다. 그룹 번호를 깔끔하게 유지하고 조금 더 빠릅니다.

Replace: 치환 문자열과 람다

Regex.Replace는 모든 일치 항목을 바꿉니다. 치환 문자열에서 $1은 그룹 1을, ${name}은 이름 있는 그룹을, $0은 일치 항목 전체를 넣습니다. 템플릿으로 표현할 수 없는 것에는 각 Match를 받아 치환 결과를 반환하는 함수(보통 람다)를 넘기세요:

출력:

Invoiced 14/03/2026, paid 02/04/2026.
too many spaces
Card **** **** **** 1234
Tea 2.75, Scone 3.52, Jam 1.10

카드 마스킹은 전방 탐색 (?=...)을 씁니다. 뒤에 숫자가 네 개 더 오는 경우에만 숫자와 일치하며, 그 네 개는 소비하지 않습니다. 탐색((?=...), (?!...), (?<=...), (?<!...))은 맥락을 일치 항목에 포함하지 않고 검사하며, 그래서 Replace 한 번으로 마지막 네 자리를 그대로 둘 수 있습니다.

패턴이 없는 평범한 텍스트에는 string.Replace가 더 간단하고 빠릅니다. 바꿀 대상이 달라질 때 Regex.Replace를 쓰세요.

Split

Regex.Split은 패턴의 모든 일치 항목에서 나누므로, 구분자가 제각각인 경우를 처리합니다:

출력:

csharp / dotnet / regex / tutorial / beginner
Order Shipped Event Handler

패턴에 캡처 그룹이 있으면 Regex.Split은 캡처된 구분자를 결과에 포함합니다. 원하지 않으면 (?:...)를 쓰세요.

RegexOptions

옵션은 패턴 전체의 동작을 바꿉니다. |로 조합하세요:

출력:

1
2
error INFO Error
True

쓰게 될 옵션:

  • IgnoreCase: 대소문자를 구분하지 않는 일치(인라인 형태 (?i)).
  • Multiline: ^와 $가 문자열 전체만이 아니라 모든 줄의 시작과 끝에서 일치합니다.
  • Singleline: .가 \n과도 일치합니다(기본적으로는 줄바꿈을 뺀 모든 문자와 일치).
  • IgnorePatternWhitespace: 패턴 안의 공백을 무시하고 #로 주석을 시작합니다. 여러 줄에 걸쳐 쓰는 긴 패턴용입니다.
  • CultureInvariant: IgnoreCase와 함께 쓰면 현재 문화권의 규칙 없이 비교합니다.
  • Compiled: 여러 번 쓰는 정규식을 위해 패턴을 한 번 IL로 컴파일합니다.

입력 검증: 앵커와 이메일 문제

검증에는 패턴을 ^와 $로 고정하세요. 그렇지 않으면 입력의 일부만 일치해도 통과합니다:

출력:

PT-1000    loose=True  strict=True
pt-1000    loose=False strict=False
XPT-1000Y  loose=True  strict=False
PT-10      loose=False strict=False
ana@example.com   True
ana@example       False
ana @example.com  False
ana@@example.com  False

이메일 패턴은 모양만 검사합니다. 공백이나 @가 없는 무언가, @ 하나, 점이 있는 도메인입니다. 의도적인 것입니다. 주소의 실제 문법(RFC 5322)은 따옴표로 감싼 로컬 부분, 주석, IP 리터럴 도메인을 허용합니다. 이를 다루려는 패턴은 수백 글자에 이르면서도 사람들이 실제로 쓰는 올바른 주소를 거부합니다. 그리고 문법적으로 완벽한 주소도 반송될 수 있습니다. 모양을 확인한 뒤 확인 이메일을 보내세요. 패턴을 직접 쓰고 싶지 않다면 System.Net.Mail.MailAddress도 모양 검사를 제공합니다.

성능: 정적 메서드, 인스턴스, 컴파일

정적 메서드(Regex.IsMatch(input, pattern))는 패턴을 파싱해 작은 캐시(기본 15개 패턴)에 보관하므로, 같은 패턴으로 반복 호출하는 비용은 작습니다. 성능이 중요한 루프에서 쓰는 패턴은 Regex 인스턴스 하나를 만들어 static readonly 필드에 두세요. 수천 번 실행된다면 느린 시작을 대가로 더 빠른 일치를 얻는 RegexOptions.Compiled를 추가하세요.

시간 제한과 파국적 역추적

어떤 패턴은 특정 입력에서 지수 시간이 걸립니다. 전형적인 예는 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!"에 대한 ^(a+)+$ 같은 중첩 수량자입니다. 엔진이 포기하기 전에 a들을 두 + 사이에 나누는 모든 방법을 시도합니다. 웹 서버에서는 사용자가 보낸 그런 입력 하나가 CPU 코어 하나를 붙잡아 둘 수 있습니다("ReDoS" 공격). 신뢰할 수 없는 입력을 다루는 정규식에는 시간 제한을 주세요:

var pattern = new Regex(@"^(\w+\s?)*$", RegexOptions.None, TimeSpan.FromMilliseconds(100));
try
{
    bool ok = pattern.IsMatch(userInput);
}
catch (RegexMatchTimeoutException)
{
    // treat as invalid input
}

모든 정적 메서드에도 시간 제한을 받는 오버로드가 있습니다. .NET 7은 일부 기능(탐색, 역참조)을 포기하는 대신 선형 시간을 보장하는 엔진인 RegexOptions.NonBacktracking도 추가했습니다.

GeneratedRegex(.NET 7)

.NET 7은 컴파일 시점에 패턴을 평범한 C# 코드로 바꾸는 소스 생성기를 추가했습니다. 시작 비용 없이 Compiled의 속도를 얻고, 생성된 코드를 IDE에서 읽을 수 있으며, 트리밍과 Native AOT에서도 동작합니다:

public static partial class Patterns
{
    [GeneratedRegex(@"^[A-Z]{2}-\d{4}$", RegexOptions.IgnoreCase)]
    public static partial Regex ProductCode();
}

bool ok = Patterns.ProductCode().IsMatch("pt-1000");   // True

.NET 7 이상에서는 컴파일 시점에 알려진 모든 패턴에 이 형태가 권장됩니다.

흔한 실수

  • 검증에서 앵커 빠뜨리기. ^...$가 없으면 IsMatch는 일치 항목을 담고 있기만 한 입력도 받아들입니다.
  • 패턴에 일반 문자열 쓰기. "\b"는 단어 경계가 아니라 백스페이스입니다. @"..."를 쓰세요.
  • 이스케이프하지 않은 특수 문자. .는 모든 문자와 일치하므로, 패턴 3.50은 3x50과도 일치합니다. \.로, 사용자 입력은 Regex.Escape로 이스케이프하세요.
  • 탐욕적 수량자. <b>bold</b>에 대한 <.*>는 문자열 전체와 일치합니다. 게으른 .*?나 부정 클래스 [^>]*를 쓰세요.
  • 신뢰할 수 없는 입력에 시간 제한 없음. 중첩 수량자가 요청을 멈추게 할 수 있습니다.
  • 구조화된 형식에 정규식 쓰기. HTML, JSON, 따옴표가 있는 CSV에는 패턴이 아니라 파서가 필요합니다.

자주 묻는 질문

C#에서 정규식은 어떻게 쓰나요?

using System.Text.RegularExpressions;를 추가하고 Regex의 정적 메서드를 호출하세요. Regex.IsMatch(input, pattern)은 bool을 반환하고, Regex.Match는 첫 일치 항목을, Regex.Matches는 모든 일치 항목을 반환하며, Regex.Replace는 치환하고 Regex.Split은 나눕니다. 백슬래시가 그대로 정규식 엔진에 전달되도록 패턴은 축자 문자열 @"\d+"로 쓰세요.

C#에서 정규식 일치 항목의 그룹 값은 어떻게 얻나요?

원하는 부분을 괄호로 감싸고 match.Groups[1].Value를 읽습니다(그룹은 1부터 번호가 매겨지고, 그룹 0은 일치 항목 전체입니다). 이름 있는 그룹이 더 명확합니다: (?<year>\d{4})는 match.Groups["year"].Value로 읽습니다. 실패한 일치 항목의 그룹은 null이 아니라 비어 있으므로 먼저 match.Success를 확인하세요.

C#에서 정규식으로 텍스트를 바꾸려면 어떻게 하나요?

Regex.Replace(input, pattern, replacement)는 모든 일치 항목을 바꿉니다. 치환 문자열은 그룹을 참조할 수 있습니다. 번호 그룹은 $1, 이름 있는 그룹은 ${name}, 일치 항목 전체는 $0입니다. 템플릿으로 표현할 수 없는 로직에는 람다를 넘기세요: Regex.Replace(text, @"\d+", m => (int.Parse(m.Value) * 2).ToString()).

C# 정규식이 대소문자를 구분하지 않게 하려면 어떻게 하나요?

RegexOptions.IgnoreCase를 넘기세요: Regex.IsMatch(input, "error", RegexOptions.IgnoreCase). 옵션은 |로 조합합니다. 예를 들어 RegexOptions.IgnoreCase | RegexOptions.Multiline입니다. 패턴 안에서 (?i)로 켤 수도 있습니다.

C#에서 정규식으로 이메일 주소를 검증하려면 어떻게 하나요?

^[^@\s]+@[^@\s]+\.[^@\s]+$ 같은 패턴은 명백한 오타(@ 누락, 공백, 도메인의 점 없음)를 잡아 주며, 폼에는 보통 이것으로 충분합니다. 정규식으로 주소를 진짜로 검증할 수는 없습니다. 전체 문법은 실용적인 패턴이 처리할 수 없는 형태를 허용하고, 문법적으로 올바른 주소도 존재하지 않을 수 있습니다. 기본 모양을 확인한 뒤 확인 이메일을 보내 검증하세요.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기