개발/C# .NET

C#에서 정규식(Regular Expression) 완벽 가이드

xwing 2025. 5. 26. 11:23

정규식

정규식(Regular Expression, Regex)은 문자열 패턴을 매칭하고 조작하는 강력한 도구입니다. C#에서는 System.Text.RegularExpressions 네임스페이스를 통해 정규식 기능을 제공하며, 문자열 검증, 추출, 치환 등 다양한 작업에 활용할 수 있습니다.

정규식 기본 사용법

1. Regex 클래스 기본 사용

using System;
using System.Text.RegularExpressions;

class Program
{
    static void Main()
    {
        string text = "안녕하세요! 제 전화번호는 010-1234-5678입니다.";
        string pattern = @"\d{3}-\d{4}-\d{4}";
        
        Regex regex = new Regex(pattern);
        Match match = regex.Match(text);
        
        if (match.Success)
        {
            Console.WriteLine($"전화번호 발견: {match.Value}");
        }
    }
}

2. 정적 메서드 사용

string text = "이메일: user@example.com";
string pattern = @"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}";

// 정적 메서드를 사용한 매칭
bool isMatch = Regex.IsMatch(text, pattern);
Match match = Regex.Match(text, pattern);

Console.WriteLine($"이메일 매칭: {isMatch}");
Console.WriteLine($"발견된 이메일: {match.Value}");

주요 정규식 패턴

기본 메타문자

패턴 설명 예제

. 임의의 한 문자 (개행 제외) a.c → "abc", "axc"
* 0회 이상 반복 ab*c → "ac", "abc", "abbc"
+ 1회 이상 반복 ab+c → "abc", "abbc"
? 0회 또는 1회 colou?r → "color", "colour"
^ 문자열 시작 ^Hello → "Hello world"
$ 문자열 끝 world$ → "Hello world"

문자 클래스

// 숫자 매칭
string pattern1 = @"\d+"; // \d는 [0-9]와 동일
string pattern2 = @"[0-9]+"; // 직접 정의

// 영문자 매칭
string pattern3 = @"[a-zA-Z]+"; // 영문자
string pattern4 = @"\w+"; // 단어 문자 (영문자, 숫자, 언더스코어)

// 공백 매칭
string pattern5 = @"\s+"; // 공백, 탭, 개행 등

실용적인 예제들

1. 이메일 주소 검증

public static bool IsValidEmail(string email)
{
    string pattern = @"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$";
    return Regex.IsMatch(email, pattern);
}

// 사용 예시
string[] emails = { "test@example.com", "invalid-email", "user.name+tag@domain.co.kr" };

foreach (string email in emails)
{
    Console.WriteLine($"{email}: {(IsValidEmail(email) ? "유효" : "무효")}");
}

2. 전화번호 포맷 변환

public static string FormatPhoneNumber(string phoneNumber)
{
    // 숫자만 추출
    string digitsOnly = Regex.Replace(phoneNumber, @"[^\d]", "");
    
    // 11자리 휴대폰 번호 포맷팅
    if (digitsOnly.Length == 11)
    {
        return Regex.Replace(digitsOnly, @"(\d{3})(\d{4})(\d{4})", "$1-$2-$3");
    }
    
    return phoneNumber; // 포맷팅 불가능한 경우 원본 반환
}

// 사용 예시
string[] phones = { "01012345678", "010 1234 5678", "010-1234-5678" };

foreach (string phone in phones)
{
    Console.WriteLine($"{phone} → {FormatPhoneNumber(phone)}");
}

3. HTML 태그 제거

public static string RemoveHtmlTags(string html)
{
    return Regex.Replace(html, @"<[^>]*>", "");
}

// 사용 예시
string htmlText = "<p>안녕하세요! <strong>강조</strong>된 텍스트입니다.</p>";
string plainText = RemoveHtmlTags(htmlText);
Console.WriteLine(plainText); // "안녕하세요! 강조된 텍스트입니다."

4. 단어 추출 및 개수 세기

public static Dictionary<string, int> CountWords(string text)
{
    var wordCounts = new Dictionary<string, int>();
    
    // 단어 패턴 매칭 (영문자만)
    MatchCollection matches = Regex.Matches(text.ToLower(), @"\b[a-z]+\b");
    
    foreach (Match match in matches)
    {
        string word = match.Value;
        wordCounts[word] = wordCounts.ContainsKey(word) ? wordCounts[word] + 1 : 1;
    }
    
    return wordCounts;
}

// 사용 예시
string text = "The quick brown fox jumps over the lazy dog. The dog was really lazy.";
var wordCounts = CountWords(text);

foreach (var kvp in wordCounts)
{
    Console.WriteLine($"{kvp.Key}: {kvp.Value}");
}

고급 기능

1. 그룹 캡처 사용

string text = "생년월일: 1990-05-15";
string pattern = @"(\d{4})-(\d{2})-(\d{2})";

Match match = Regex.Match(text, pattern);
if (match.Success)
{
    Console.WriteLine($"전체 매칭: {match.Value}");
    Console.WriteLine($"년: {match.Groups[1].Value}");
    Console.WriteLine($"월: {match.Groups[2].Value}");
    Console.WriteLine($"일: {match.Groups[3].Value}");
}

2. 명명된 그룹 사용

string text = "사용자: john_doe, 나이: 25";
string pattern = @"사용자: (?<username>\w+), 나이: (?<age>\d+)";

Match match = Regex.Match(text, pattern);
if (match.Success)
{
    Console.WriteLine($"사용자명: {match.Groups["username"].Value}");
    Console.WriteLine($"나이: {match.Groups["age"].Value}");
}

3. 조건부 치환

// 대소문자 변환 예제
string text = "Hello World! This is a TEST.";
string result = Regex.Replace(text, @"\b[A-Z]+\b", 
    match => match.Value.ToLower());

Console.WriteLine(result); // "Hello World! This is a test."

성능 최적화 팁

1. 컴파일된 정규식 사용

// 반복적으로 사용되는 패턴은 컴파일하여 성능 향상
private static readonly Regex EmailRegex = new Regex(
    @"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$", 
    RegexOptions.Compiled);

public static bool IsValidEmailOptimized(string email)
{
    return EmailRegex.IsMatch(email);
}

2. 타임아웃 설정

// 복잡한 패턴에서 무한 루프 방지
try
{
    var regex = new Regex(pattern, RegexOptions.None, TimeSpan.FromSeconds(1));
    Match match = regex.Match(input);
}
catch (RegexMatchTimeoutException)
{
    Console.WriteLine("정규식 매칭 시간 초과");
}

일반적인 패턴 모음

public static class CommonPatterns
{
    // 이메일
    public static readonly string Email = @"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$";
    
    // 한국 휴대폰 번호
    public static readonly string KoreanPhone = @"^010-\d{4}-\d{4}$";
    
    // URL
    public static readonly string Url = @"https?://[^\s]+";
    
    // 숫자만
    public static readonly string NumbersOnly = @"^\d+$";
    
    // 영문자와 숫자만
    public static readonly string AlphaNumeric = @"^[a-zA-Z0-9]+$";
    
    // 한국 우편번호 (새 형식)
    public static readonly string KoreanZipCode = @"^\d{5}$";
    
    // 비밀번호 (최소 8자, 영문자와 숫자 포함)
    public static readonly string Password = @"^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$";
}

주의사항 및 베스트 프랙티스

1. Verbatim String 사용

// 권장: @ 접두사 사용으로 백슬래시 이스케이프 방지
string pattern = @"\d{3}-\d{4}-\d{4}";

// 비권장: 이스케이프 문자로 인한 복잡성
string pattern2 = "\\d{3}-\\d{4}-\\d{4}";

2. 정규식 검증 도구 활용

복잡한 정규식을 작성할 때는 온라인 도구를 활용하여 패턴을 테스트하고 검증하는 것이 좋습니다. regex101.com과 같은 사이트에서 실시간으로 패턴을 테스트할 수 있습니다.

3. 성능 고려사항

  • 정규식은 강력하지만 복잡한 패턴은 성능에 영향을 줄 수 있습니다
  • 단순한 문자열 연산으로 해결 가능한 경우는 String 클래스의 메서드를 우선 고려하세요
  • 반복적으로 사용되는 패턴은 RegexOptions.Compiled 옵션을 사용하세요

마무리

C#의 정규식은 문자열 처리에서 매우 유용한 도구입니다. 기본적인 패턴 매칭부터 복잡한 데이터 추출까지 다양한 용도로 활용할 수 있습니다. 하지만 정규식의 복잡성으로 인해 가독성과 유지보수성을 고려하여 적절히 사용하는 것이 중요합니다.

정규식을 마스터하면 문자열 처리 작업의 효율성을 크게 향상시킬 수 있으니, 다양한 패턴을 연습해보시기 바랍니다.