본문 바로가기

개발/정규표현식

숫자표현 정규식 스텝by스텝

최근 너무 긴 글을 쓰다 보니, 새로 장문의 포스팅을 하기가 조금 망설여졌다.

 

그래서 오늘은 좀 쉬어간다는 생각으로, 조금 간단하지만 꽤 빈번하게 쓰이는 숫자로 이루어진 문자열을 검사하기 위한 정규표현식에 대해 단계적으로 알아보도록 하겠다.

 

기초적인 부분에 대한 설명은 생략될 수 있는데, 이미 여러분들이 정규식을 가볍게라도 한 번쯤 경험해봤다는 가정하에 최대한 간단명료하게 설명하도록 하겠다.

 

그리고 앞으로 모든 정규식 관련 실습은 Rubular 사이트를 통해 진행하려 한다. 이곳은 내 개인적으로 개발업무에 필요한 정규식을 작성할 때 주로 방문하는 사이트다.

 

Ruby 기반 정규표현식이긴 하지만, 타 언어 표현식과도 사실 크게 다르지 않아 쉽게 응용이 가능한 편이고, 페이지 하단의 Regex quick reference 은 정규식이 익숙지 않은 사람들에게 올바른 사용법을 다시금 상기시켜주기도 한다.

 

그리고 무엇보다 가장 유용하다 생각한 포인트는, 화면 중앙에 위치한 make permalink 기능에 있다.  

이 링크 생성 버튼을 누르면, 지금껏 작성한 정규표현식과 하단의 테스트 문자열이 고스란히 저장된 url링크를 만들어준다.

 

이 편리함은 무엇인가.

스크린샷에 보이듯이, 해당 작업의 url링크가 생성된 것을 볼 수 있다. 협업 중인 동료들과 해당 링크를 공유하며, 관련 피드백을 주고받을 때 정말 유용한 기능이다.

 

 

 

 

그럼, 완전 심플 버전부터 최종 버전까지... 조금씩 요구사항을 늘려가며 정규식을 고쳐나가보자.

 

step 1) 완전 심플 버전.


^[0-9]+$

^\d+$
0314
1931381

둘 다 결국 같은 표현식이다. 정말 단순하고 심플하지 않은가. 0~9로 이루어진 1자리 이상의 숫자면 그냥 통과다.

 

그런데, 여기서 살짝 보완할 점이 보인다. 한자리 숫자 '0' 이 아닌데, '0314' 같이  '0'부터 시작하면 이상하다. 수정해보자.

 

step 2) 약간 똑똑해진 버전.


^(0|[1-9]+[0-9]*)$

 

step 2) 아주 약간 똑똑해졌다.

 

단일 숫자 문자열 '0' 이거나, 가장 왼쪽 숫자는 1-9까지만 나올 수 있고 그다음에는 0-9인 숫자가 0개 이상 나올 수 있다는 표현식.

 

Match result 를 살펴보면 알 수 있듯이 ,  '034' 와 같이 가장 왼쪽 문자열이 0으로 시작되는 숫자 문자열은 허용하지 않는다.

 

조금은 똑똑해졌는데... 어라? 음수(-)는 어쩌지? 양수뿐 아니라 음수(-)도 케어해주자.

 

step 3) 음수도 되는 약간 똑똑한 버전.


바꾸는 김에 살짝 다듬어보자. [1-9]+ 에 붙은 + 표시는 사실 뒷부분과 중복되는 표현이라 빼버려도 되고, [0-9]까지의 숫자 허용은 \d 와 같다. 여기에, 가장 앞에 음수기호(-) 가 있을 수도 있다는 표현식 [-]? 이걸 추가하자.

^(0|[-]?[1-9]\d*)$

 

step 3) 이제 음수도 된다.

'-906' 은 되지만,  '-0' 이런 숫자 문자열은 당연히 허용하면 안 된다. 안되고 말고.

 

여기까지 왔으면 사실상 남은 부분은 하나밖에 없다. 소수의 소수점 부분도 표현가능한 정규식을 작성해보자.

 

만약, 소수는 검사할 필요 없다. 정수만 숫자로 판단한다! 싶으면  step 2) 버전으로 충분하다.

 

step 3) 소수도 되는 최종 버전.


음... 뒤에 간단히 소수점(.) 문자 뒤로 숫자 문자열이 있거나 없거나라는 표현식만 표기하면 되지 않을까? 일단 가볍게 추가해본다.

^(0|[-]?[1-9]\d*)(?:\.\d+)?$

 

결과를 보니, 뒤쪽에 단순히 (?:\.\d+)?$ 로 케어하지 못하는 부분이 생겼음을 알 수 있다.

 

그런데 위 정규식을 살펴보면, 괄호안에 ?: 기호를 보았을 것이다. 이는 해당 괄호영역은 비캡쳐링그룹(non-capturing group) 이라는 표현식이다.

 

캡쳐링그룹?          

괄호로 묶이는 영역은 캡쳐링그룹(capturing group)이라고 해서, 각각의 분리된 영역을 가진다.

 

여기서 각 그룹은 순차적으로 그룹번호를 부여받는데, 이를 활용하면 패턴 안의 원하는 부분을 쉽게 분리하거나 문자열을 치환하는데 사용할 수 있다. 이전 포스팅에서 티스토리 2차블로그 주소 변경에 사용했었는데, 사용 예가 궁금하다면 잠깐 살펴보자.

2020/03/09 - [개발/정규표현식] - 티스토리 2차 도메인 자동이동 정규식 스크립트

 

비캡쳐링그룹?       

간단히 설명하자면, 캡쳐링그룹 배열에 포함되지 않게 한다. 괄호안 가장 앞 부분에 ?: 기호를 표시해 적용하는데, 별도로 그룹화할 필요가 없을 때  주로 쓰인다. 

 

 

이제 다시 돌아와서 결과를 살펴보도록 하자.

 

다 좋은데, -0.2 는 안된다.

다른 부분은 잘 통과했지만, -0.2 이 녀석이 안된다. 앞쪽에 설명했듯이 음수 표현식에서 '-0' 와 같이 숫자 0에 음수기호(-)는 허용하지 않았기 때문이다. 지금처럼 소수를 포함하는 정규식이 되면 이야기가 달라진다. 

 

 

별 수 없다. 많이 더럽지만, 한 땀 한 땀 해당되는 케이스를 풀어서 표현해보자. 나중에 좀 더 깔끔한 정규식이 생각나면 수정해두자.

^(0|(([-][0]\.\d+)|([0]+\.\d+)|([-]?[1-9]\d*(\.\d+)?)))$

단순히 나올 수 있는 모든 경우를 풀어서 표현해보았다.  위 정규식을 차례대로 살펴보면 아래와 같다.

'0' 단일 문자.

-0.xx 과 같은 형식의 음의 소수인 경우.

0.xx 과 같은 형식의 양의 소수의 경우.

그 외 모든 경우. 가장 왼쪽 문자열이 '0'으로 시작하지 않는 모든 정수, 소수표현.(음수 포함)

 

step 3) 최종 버전 - 더럽지만 잘 동작한다?

... 하고자 하면 안되는 건 없다. 다만 조금 깔끔하지 않거나, 중복검사되는 부분이 있어 성능상 불이익이 조금 있을 수 있겠지만 말이다.

 

 

마치며...


정규식에 정해진 정답은 없다. 위에서 작성해본 정규식은 숫자를 표현하는 문자열을 검사하는 다양한 정규표현식 중 하나일 뿐이며, 좀 더 직관적이고 심플한 표현식이 있을 수 있다.

 

부족하나마 도움이 되었길 바라며, 이번 포스팅을 마친다.