728x90
반응형
정규식의 미리보기/돌아보기 기능은 삽질중독재활센터나 정규식이야기 원고에 없던 내용이다. 솔직히, 너무 고급스런 기능으로 비춰지지 않을까 고민을 하다가... 그냥 넣기로 하고 오늘 원고에 추가했다. 사실, 정규식은 미리보기/돌아보기까지 쓸 줄 알면 전문가나 다름없다. 나머지는 역시... 짱구다. 짱구 굴리는 만큼 보답을 해주는게 정규식이니까...
 
 
정규식의 미리보기/돌아보기 기능
 
 
정규식으로 패턴 인식을 할 때 컴퓨터는 주어진 문자열을 왼쪽에서 오른쪽으로 읽어나간다. 예를 들어, 다음 단락에서 대두란 단어를 찾는다면 첫번째 문자부터 마지막 문자까지 사람이 글을 읽어나가듯 훑어내려간다는 말이다.


제 필명은 대두족장입니다. 대두족장이라고 불러주세요. 대두족의 일원이랍니다. 머리가 커서 대두라는 거지 대두콩은 아니구요. 전 대두족의 족장입니다. 다른 족은 몰라도 대두족만큼은 절 족장으로 인정할 수밖에 없지요.

 

왼쪽에서 오른쪽으로 읽어나가며 검색을 하는 건 일반 찾고 바꾸기 역시 마찬가지다. 이 방식의 한가지 단점은 일방통행이라 한번 지나가면 돌아오지 못한다는 거다. 예를 들어, 앞에 나온 단락에서 대두 라는 단어를 찾는데 대두족장 이라는 단어의 일부로 쓰인 경우만 찾는다면 어떻게 할까?


한 문자씩 오른쪽으로 이동하다 보면 대두 라는 단어를 만나게 된다. 또, 족장 이란 단어까지 지나가 봐야 앞에서 찾은 대두 라는 단어가 대두족장 의 일부라는 것도 알게 된다. 하지만, 이미 지나와 버렸기 때문에 대두족장 의 일부로 대두 라는 단어가 쓰인 건지 확인할 길이 없다. 물론, 사람이라면 대두 가 나왔을 때 다음에 족장 이 나오는지 확인하고 다시 돌아와서 대두 를 기억해 두겠지만 일방통행인 컴퓨터는 이게 안된다는 거다.

 

일반 찾고 바꾸기에서는 안되지만 다행히도 정규식에서는 방법이 있다. 컴쟁이들은 불편한 거 못참는단 말이다.

 

바로 미리보기(lookahead)돌아보기(lookbehind) 기능이다. 지금까지 나왔던 특수문자보다 조금 어렵게 생겼지만 원리는 똑같다.


미리보기 찾기 패턴: 대두(?=족장)


미리보기를 하려면 (?= 로 시작해서 ) 로 감싸줘 미리 확인하려는 패턴을 지정하는 거다. 괄호로 감싸주는데 그 시작을 물음표와 등호표시(?=) 를 붙여 줘야 한다는 말이다. 미리보기는 특정 패턴을 찾되 또 다른 패턴이 뒤따라올 때만 찾으라고 조건을 다는 거다. 앞의 경우는 대두 라는 단어를 만났을 때 뒤따라 오는 말이 족장 인 경우만 찾아달라는 의미가 된다. 대두족장 편집기에서 확인해 보자.

 


미리보기 조건을 거꾸로 할 수도 있다. 예를 들어, 대두 를 찾되 뒤따라오는 말이 족장 이 아닌 경우만 찾으라고 조건을 거는 거다. 이때는 물음표 다음 기호가 등호가 아닌 느낌표(!)가 된다.


반대 조건 미리보기 패턴: 대두(?!족장)
 
조건을 달 때 등호(=)는 조건을 만족하는 경우를 나타내고 느낌표(!)는 주어진 조건을 부정하는 걸 나타낸다. 그러니까 대두(?=족장) 이란 표현은 대두 라는 단어가 나왔을 때 ‘뒤 따라 오는 패턴이 족장이어야 한다’는 긍정적인 조건을 만족하면 찾으라는 말이고 대두(?!족장) 이라고 하면 ‘뒤 따라 오는 패턴이 족장이 아니어야 한다’는 부정적인 조건을 만족하면 찾으라는 말이 된다. 정규식 용어로는 전자를 긍정적 미리보기(positive lookahead), 후자를 부정적 미리보기(negative lookahead)라고 한다.


미리보기가 사용되는 경우를 몇 가지 살펴보면 다음과 같다.


 

뒤따라 오는 단어가 공백+Alba 인 경우만 Jessica 를 찾기: Jessica(?= Alba)

한영과 학번이 KE9324 식으로 돼 있을 때 한영과 학번만 찾기: KE(?=[0-9]{4})

삼성이라는 표현중 삼성전자의 일부가 아닌 경우만 찾기: 삼성(?!전자)

 

돌아보기는 미리보기와 반대로 움직인다. 미리보기는 특정 패턴이 나왔을 때 그 다음 패턴을 미리 보고 조건을 따지는 반면, 돌아보기는 특정 패턴이 나왔을 때 그 이전 패턴을 돌아보고 조건을 따지는 것만 다르다. 대두족장의 예를 돌아보기로 써보면 다음과 같다.


돌아보기: (?<=대두)족장


 

족장 이라는 패턴을 찾되 앞에 나오는 말이 대두 여야 한다는 조건을 다는 거다.


미리보기처럼 돌아보기도 앞에 나온 경우처럼 긍정적 조건을 내 걸면 긍정적 돌아보기(positive lookbehind)라고 하고 부정적 조건을 내건 경우는 부정적 돌아보기(negative lookbehind)라고 한다. 부정적 돌아보기의 예는 다음과 같다.

 

부정적 돌아보기: (?<!대두)족장


 


미리보기와 마찬가지로 긍정적 조건을 내걸 때는 등호(=)를 쓰고 부정적 조건을 내걸 때는 느낌표(!)를 쓰는 거다.


앞에 나온 미리보기 적용 사례들을 돌아보기로 응용하면 다음처럼 된다.


앞에 나온 단어가 Jessica+공백 인 경우만 Alba를 찾기: (?<=Jessica )Alba

한영과 학번이 KE9324 식으로 돼 있을 때 한영과 학번 숫자 찾기: (?<=KE)[0-9]{4}

전자라는 표현 중 삼성전자의 일부가 아닌 경우만 찾기: (?<!삼성)전자

 

미리보기와 돌아보기에서 한가지 주의할 점은 이 패턴에 사용되는 괄호는 역참조에 활용할 수 없다는 거다. 다시 말해, 여기서 괄호는 미리보기와 돌아보기를 표시하기 위한 기호일 뿐 찾아낸 패턴을 기억해 역참조에 활용하는 기능은 없다는 말이다.

 

누누히 강조하지만 정규식은 짱구의 산물이요 짱구만이 정규식의 빠우어~를 이끌어낼 수 있다.

 

 

천단위 콤마찍기 예제

 

 

미리보기 돌아보기 이딴거 어따쓰냐고 고민하지 말고 짱구를 굴려보란 말이다. 컴쟁이들의 첨단 짱구하나 소개한다. 미리보기/돌아보기로 천단위 콤마를 찍는 예다. 미리보기/돌아보기 하면 꼭 나오는 예지만 언제봐도 컴쟁이들의 짱구에 탄복하게 된다.

 

이런 식으로 숫자가 무더기로 나와 있는 문서가 있다고 생각해 보자.


당기 매출액: 1234578900 원
전기 매출액: 1335560000 원
당기 순이익: 346588900 원
전기 순이익: 416508700 원

 

천단위 콤마가 없으면 숫자가 길어질 때 참 읽기 힘들다. 그렇다고 또 저걸 일일이 손으로 천단위 씩 끊어가며 콤마를 넣어주기엔 우리가 너무나 호모 사피엔스스럽지 않은가. 이럴 때도 정규식을 쓸 수 있느냐구?


당근이다. 정규식은 패턴인식되는 곳이라면 어디든지 달려가는 홍반장이니까.


먼저, 천단위 콤마라는 걸 패턴으로 인식해 보자. 막연히 ‘천단위 콤마찍기’식으로 생각하면 문제 인식이 어렵다. 천단위 콤마 찍기를 패턴화해서 이 문제를 모르는 누군가에게 설명하듯 풀어놓는 게 가장 먼저해야 할 일이라는 거다.

 

패턴 인식: 10000000 식으로 숫자가 있을 때 가장 마지막 숫자부터 시작해서 왼쪽으로 숫자를 세어가며 세번째 위치마다 콤마를 넣어준다.


이게 천단위 콤마의 패턴이다. 정규식으로 가장 오른쪽 숫자부터 숫자 3개 단위로 끊어가며 세자리 수 패턴을 찾아 기억시키고 해당 위치마다 쉼표를 넣어주면 될 듯 하다.


그런데 컴퓨터는 정규식을 왼쪽에서 오른쪽으로 한 문자씩 읽어가며 적용한다는 문제가 있다. 천단위 콤마는 가장 오른쪽 숫자부터 왼쪽으로 거꾸로 읽어서 해결해야 하는데 컴퓨터는 그 반대 방향으로 움직인다는 거다.


1000 이라는 숫자를 예로 들어보자. 사람은 뒤쪽부터 시작해서 000 이 지난 다음 10 사이에 콤마를 찍어 1,000 을 만든다. 하지만 컴퓨터는 1부터 시작해서 1->0->0->0 식으로 읽어나가기 때문에 거꾸로 확인할 수가 없다.


컴쟁이이들이 이 단점을 극복하기 위해 정규식에 도입한 게 미리보기 및 돌아보기 기능이라는 말이다. 사람에게 컴퓨터처럼 1000 이라는 숫자를 왼쪽에서 오른쪽으로 읽어나가면서 천단위 콤마를 찍으라고 하면 어떻게 할까? 호모 사피엔스이니 이렇게 할 거다.


1. 첫번째 숫자는 콤마가 앞에 올 수 없으니 그냥 넘어간다.


2. 1을 뒤따라 오는 0을 만나면 첫번째 숫자가 아님을 확인하기 위해 앞에 숫자가 나오는지 돌아본다. 앞에 1이 있으므로 첫번째 숫자가 아니다.


3. 뒤따라오는 숫자와 현재 숫자를 합쳐 나머지 숫자가 모두 3자리 단위로 끊어지는 지 확인한다. 뒤에 00이 있으므로 현재 0 과 합쳐 000 이 돼서 3자리 단위로 숫자가 딱 맞아 떨어진다. 따라서, 현재 숫자 앞에 콤마를 찍어준다.

1번과 2번의 조건을 정규식 돌아보기로 나타내면 다음과 같다.


앞자리에 숫자가 나와야 한다: (?<=[0-9]) 또는 (?<=\d)


3번째 조건은 다음 정규식 패턴으로 확인할 수 있다.


숫자가 3자리 단위로 끊어지는 지 확인한다: (?=([0-9][0-9][0-9])+) 또는 (?=([0-9]{3})+) 또는 (?=(\d{3})+)


3자리 숫자 단위로 끊어진다는 말은 결국 000 식으로 숫자가 3개 붙어 있는 패턴이 연속된다는 말이다. 다음 두 정규식 패턴은 전혀 다른 의미라는 거다.


3자리 단위로 끊어 보기 올바른 정규식 패턴: (?=([0-9][0-9][0-9])+)

3자리 단위로 끊어 보기 잘못된 정규식 패턴: (?=([0-9]+)


올바른 정규식 패턴은 000 이든 000000 이든 000000000 이든 3의 배수로 숫자가 이어지면 찾아주지만 잘못된 정규식 패턴은 숫자가 1개 이상 반복되면 무조건 찾아주기 때문에 3자리의 의미가 없어진다는 말이다.


따라서, 최종 찾기 패턴은 이렇게 된다.


최종 찾기 패턴: (?<=[0-9])(?=([0-9][0-9][0-9])+) 또는 (?<=\d)(?=(\d{3})+)


패턴을 잘 보면 가장 왼쪽 숫자부터 시작해서 오른쪽으로 이동해가며 현재 위치에서 왼쪽 문자가 숫자인지 돌아보기로 확인 (?<=\d) 을 한다. 또, 오른쪽으로 한 문자씩 이동하면서 미리보기를 통해 나머지 숫자가 3단위로 끊어지는 지 확인 (?=(\d{3})+) 한다. 결국 해당 패턴을 찾았을 때 컴퓨터가 바라보는 곳은 언제나 1234 의 경우 1과 234의 경계선에 해당하는 위치라는 거다. 돌아보기와 미리보기의 경계선! 그 경계선에 콤마를 넣어주면 되기 때문에 바꾸기 패턴 역시 달랑 콤마 하나면 그만이다.


바꾸기 패턴: ,

다시 말해, 어떤 문자나 숫자가 아닌 위치(anchor)만 이동한 것이기 때문에 그 위치와 콤마를 맞바꾸는 것이고 원문에는 콤마가 생겨날 뿐 있던 문자가 사라지는 일은 없다는 거다.


그럼 찾고 바꾸기를 해보자.

 


아쉽게도 원하는 결과가 아니다. 앞에 나온 찾기 패턴은 4자리수의 경우는 정확히 콤마를 찍어주지만 5자리부터는 무조건 한 숫자당 한 개씩 콤마를 찍어버린다.

 

왜 그럴까?


12345 를 예로 들면 1->2까지 왔을 때 미리보기 조건(직전 문자가 숫자여야 한다)과 돌아보기 조건(뒤 따르는 문자가 숫자 3개 이상의 패턴이어야 한다)을 모두 만족한다. 미리보기 조건은 문제가 안되는 데 돌아보기 조건에 이상이 있는 거란 말이다. 1->2가 됐을 때 뒤따르는 2개 숫자를 합쳐 234 가 되고 ‘3개 이상의 숫자가 1번 이상 반복되는 패턴’ 이라는 ([0-9][0-9][0-9])+ 조건을 만족하기 때문에 콤마가 찍히는 거다.

 

이 조건을 수정해 보자. 3자리 단위로 숫자가 딱 떨어지게 만들려면 어떻게 해야 할까? 숫자 3자리 패턴이 1번 이상 반복되지만 딱 떨어지려면 더 이상 숫자가 남지 않으면 되지 않을까? 다시 말해, 12345에서 1->2가 됐을 때 234 로 숫자 3단위가 만들어지지만 마지막 숫자 5가 남으면 안되는 조건을 만들면 된다는 거다.


3자리 단위로 끊어 보기 수정 정규식 패턴: (?=([0-9][0-9][0-9])+(?![0-9])) 또는 (?=(\d{3})+(?!\d))


따라서, 천단위 콤마찍기 최종 찾기 패턴은 다음과 같다.

 

최종 찾기 패턴: (?<=[0-9])(?=([0-9][0-9][0-9])+(?![0-9])) 또는 (?=\d)(?=(\d{3})+(?!\d))

 

편집기에서 확인해 보자.

 


 
빤따스띡~~~ 한 결과다.

 

천단위 콤마 찍기 정규식 패턴을 이해할 수 있다면 여러분도 정규식 전문가나 다름없다.

 

정규식을 어따쓸까...

 

짱구 굴리기 나름이다.

 

앞으로도 틈나는대로 같이 짱구 굴려보겠다^^

728x90
반응형
블로그 이미지

nineDeveloper

안녕하세요 현직 개발자 입니다 ~ 빠르게 변화하는 세상에 뒤쳐지지 않도록 우리모두 열심히 공부합시다 ~! 개발공부는 넘나 재미있는 것~!

,