[C#] Regex(정규식, 정규표현식)

PikminProtectionAssociation·2024년 11월 7일

행성 탈출기

목록 보기
8/21

얼마 전 응시했던 코딩테스트에서 정규식을 활용해서 풀 수 있을 것 같은 문제가 나왔다
근데 이제 문제는 다익스트라, 플로이드, 벨만-포드, MST, LIS 등등 이것저것 다 복습해놓고 정작 Regex는 까먹은 나애요.

문제 보자마자 쇼타로가 되.


결국 인덱스 갖고 풀다가 기억을 되짚어서 Regex 살짝 끄적여보다가 뭔 오류가 자꾸 나서 포기하고 인덱스로 다시 풀다가 끝나버렸다

이참에 정리하면서 다시 제대로 공부해보기로!!



Regex (정규식)

정의

  • 정규식은 텍스트를 처리하는 효율적인 방법을 제공
  • 패턴 일치 표기법을 통해 많은 양의 텍스트를 빠르게 구문 분석할 수 있음
    • 특정 문자 패턴 찾기
    • 텍스트 유효성 검사를 통해 미리 정의된 패턴과 일치 여부 확인
    • 텍스트 하위 문자열 추출, 편집, 변경, 삭제
    • 추출된 문자열을 컬렉션에 추가

메서드

  • Regex.IsMatch : 입력 텍스트에서 정규식 패턴이 발생하는지 확인
  • Regex.Math / Regex.Matches : 정규식 패턴과 일치하는 텍스트를 하나 또는 모두 검색
  • Regex.Replace : 정규식 패턴과 일치하는 텍스트를 변경

문법

메타문자기능예시
?0번 또는 1번의 발생colou?r => color, colour
*0번 이상의 발생ab*c => ac, abc, abbc, abbbc
+1번 이상의 발생ab+c => abc, abbc, abbbc (ac는 불일치)
{n}정확히 n번만큼 일치
{min,}최소 min번 이상 일치
{min,max}min번 이상 max번 이하 일치
.1개의 문자와 일치
[ ]"["과 "]" 사이의 문자 중 하나를 선택[abc]d => ab, bd, cd
[a-z] = a부터 z까지 중 하나
[^ ]문자 클래스 안의 문자를 제외한 나머지 문자를 선택[^abc]d => ed, fd
^, \A문자열이나 행의 처음을 의미
$, \z문자열이나 행의 끝을 의미
( )여러 식을 하나로 묶음abc|adc와 a(b|d)c는 같은 의미
\n일치하는 패턴 중 n번째를 선택(1≤n≤9)
\d10진수 숫자
\D숫자가 아닌 문자
\b단어 경계
\B단어가 아닌 경계
\s공백 문자
\S공백이 아닌 문자
\w단어 문자
([a-zA-Z_0-9]와 동일할 수 있음)
\W단어가 아닌 문자

플래그

flag의미설명
iIgnore Case대소문자를 구별하지 않고 검색
gGlobal문자열 내의 모든 패턴의 정규식 검색
mMulti Line문자열의 행이 바뀌어도 정규식을 검색
s정규식 검색 시 \n도 포함하여 검색
uUnicode정규식 검색 시 유니코드 지원
ySticky문자 내 특정 위치에서 검색을 진행하는 'sticky' 모드 활성화

예제

백준 1013 Contact

문제

푸에르토리코 아레시보에 위치한 아레시보 전파망원경(Arecibo radio telescope)은 수십 년째 존재하지 않을 지도 모르는 외계 문명으로부터의 전파를 수신하기 위해 밤하늘을 바라보고 있다.
이 망원경이 수집한 전파 속에서 자연적으로 발생하기 힘든 패턴들을 찾아내어, 그것을 증거로 외계 문명의 존재 여부를 가리려는 노력은 줄곧 이어져왔지만 아직까지도 그러한 패턴은 발견되지 않았다. 한국 천문학계의 자존심 김동혁 박사는 국내 기술로 이러한 탐사를 진행하기 위하여 다음의 전파 표기를 표준으로 삼았다.
전파의 기본 단위는 { 0 , 1 } 두 가지로 구성되어있으며, x+ ( ) 는 임의의 개수(최소 1개) x의 반복으로 이루어진 전파의 집합을 나타낸다.
(xyx)+ ( ) 는 괄호 내의 xyx의 반복으로 이루어진 전파의 집합을 뜻한다. 아래는 이해를 돕기 위한 예제이다.

1+ = { 1, 11, 111, 1111, 11111, … }
10+ = { 10, 100, 1000, 10000, 100000, … }
(01)+ = { 01, 0101, 010101, 01010101, 0101010101, … }
(1001)+ = { 1001, 10011001, 100110011001, … }
10+11 = { 1011, 10011, 100011, 1000011, 10000011, … }
(10+1)+ = { 101, 1001, 10001, 1011001, 1001101, 100011011000001, … }

반복을 의미하는 + 외에도 or 를 의미하는 | 기호가 있다. { x | y } 는 x 혹은 y 를 의미하는 것으로, { 0+ | 1+ } 는 { 0 , 1 , 00 , 11 , 000 , 111 , … } 의 집합을 의미한다. 아래는 두 기호를 복합적으로 사용한 예이다.

(100 | 11)+ = { 100 , 11 , 10011 , 11100 , 1110011100 , 100111111100100, … }

최근 김동혁 박사는 아레시보 전파망원경에서 star Vega(직녀성) 으로부터 수신한 전파 기록의 일부를 조사하여 그 전파들의 패턴을 분석하여 아래와 같이 기록하였다.

(100+1+ | 01)+

김동혁 박사는 다양한 전파 기록 중에서 위의 패턴을 지니는 전파를 가려내는 프로그램을 필요로 한다. 이를 수행할 수 있는 프로그램을 작성하라.

입력

입력의 첫 줄에는 테스트 케이스의 개수 T가 주어진다. 그 다음 줄부터 각각의 테스트 케이스에 대해 전파를 표현하는, { 0, 1 }만으로 이루어진 문자열이 공백 없이 주어진다. 문자열 길이는 (1 ≤ N ≤ 200)의 범위를 갖는다.

출력

각 테스트 케이스에 대해 주어진 전파가 문제에서 제시한 패턴이면 “YES”를 그렇지 않은 경우는 “NO”를 출력한다. 출력 문자열은 모두 대문자로 구성되어 있다.


생각

  • 문제 조건부터 온몸으로 정규식이라고 외치고 있다.
  • 실제로 정규식에서 '+'는 1개 이상의 반복을, '|'는 or을 의미한다.

해결

  • 정규식을 쓰자!

코드

static void Main(string[] args)
{
	const string YES = "YES";
    const string NO = "NO";
    
    Regex regex = new Regex(@"^(100+1+|01)+$");
    
    int T = int.Parse(Console.ReadLine());
    
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < T; i++)
    {
    	if (regex.IsMatch(Console.ReadLine()))
        {
        	sb.Append(YES).Append("\n");
        } else
        {
        	sb.Append(NO).Append("\n");
        }
    }
    
    Console.WriteLine(sb.ToString());   
}
  • 주의할 점은 "(100+1+|01)+"을 그대로 사용하면 모든 예제가 YES로 나온다는 것이다.
    • IsMatch를 통해 타겟 string 속에 원하는 패턴이 있는지 없는지 여부를 판단하기 때문
    • 문제에서 원하는 조건은 처음부터 끝까지 패턴으로 이루어져 있는지 여부를 판단하는 것
  • 그래서 문자의 시작과 끝을 의미하는 "^", "$"를 사용해줬다.
    근데 다른 방법이 있을수도
    내가 모를수도
    떠올리지 못했을수도
    내가 무지했을수도
    내가 감히
    내가 또 잘못을


출처

Microsoft Learn .NET 정규식
C# 정규식을 사용하여 패턴 매칭(Regex, IsMatch)
정규 표현식(Regular Expression)



아무튼 교훈은 문자열 문제 풀이를 소홀히 하지 말자는 것 ^ㅠ^

끗!!

0개의 댓글