서론
최근 정규표현식이 많이 부족하다는 걸 느꼈습니다.
이번 기회에 제대로 알아보자! 결심하고 정규표현식에 대해 공부해봤습니다.
정규표현식 개념보다는 예제 위주로 포스팅을 작성하였습니다.
정규표현식
정규표현식(regular expression, regex)이란 사용자가 규칙을 세워 패턴을 정의해둔 문자열입니다.
정규표현식은 문자열의 일부나 전체가 패턴이 일치하는지 알아볼 수 있는 편리한 방법입니다.
패턴이 일치하는 문자열만 추출할 때, 문자열이 패턴과 일치하는지 알고 싶을 때 등의 상황에서 유용하게 사용할 수 있습니다.
정규표현식 문법
문법 | 기능 | 설명 |
. | 문자 일치 | 개행 문자를 제외한 문자 1개와 일치 |
[ ] | 문자 집합 | : 를 여러 번 쓴 것과 동일함. 사이에 들어온 문자를 선택하고 - 기호를 통해 범위를 지정함. [a-z]는 a부터 z까지 중 하나를 의미함 |
[^] | 문자 집합 부정 | 문자 집합이 아닌 문자. [^a-z]는 알파벳 소문자를 제외한 모든 문자 |
^ | 시작 | 문자열의 시작을 의미함 |
$ | 끝 | 문자열의 끝을 의미함 |
( ) | 병합 | 여려 표현식을 하나로 묶을 때 사용함 a(b|c)d는 abd 또는 acd임 |
\n | n번 째 패턴 | 일치하는 여러 패턴 중 n번 째를 의미함. 1 ~ 9까지의 자연수만 올 수 있음 |
* | 0개 이상 포함 | 0개 이상의 문자를 포함함. a*b는 b, ab, aab 등을 의미함 |
{m, n} | m개 이상 n개 이하 | m개 이상, n개 이하의 문자를 포함함 |
? | 0~1개 | 0 ~ 1개 포함해야 함 |
+ | 1개 이상 | 1개 이상 |
| | or | x | y인 경우, x 또는 y를 의미함 |
\w | 영숫자, _ | [A-Za-z0-9_]와 동일. 영어, 숫자, _ 문자 |
\W | 영숫자, _ 부정 | [^A-Za-z0-9_]와 동일. 영어, 숫자, _가 아닌 다른 문자 |
\d | 숫자 | [0-9]와 동일. 숫자를 의미함 |
\D | 숫자 부정 | [^0-9]와 동일. 숫자가 아닌 문자 |
\s | 공백 | 공백 문자 |
\S | 공백 부정 | 공백이 아닌 모든 문자 |
이외 다른 문법도 있지만 자주 사용되는 문법만 추렸습니다.
다른 문법은 https://developer.apple.com/documentation/foundation/nsregularexpression 에서 확인 가능합니다.
정규표현식 만들기
정규표현식 예제를 알아보기 전에 Swift에서 정규표현식을 어떻게 만드는지 알아보겠습니다.
let pattern1 = "[a-z]" //String
let pattern2 = /[a-z]/ //Regex<Substring>
let pattern3 = #"[a-z]"# //String
Swift에서는 세 가지 방법이 있습니다.
1과 3은 String 타입으로 만들어지고, pattern2는 Regex<Substring> 타입으로 만들어집니다.
String 타입은 try Regex(string)을 이용해 Regex 타입으로 만들 수 있답니다.
(이번 포스팅에서는 정규표현식 예제 위주로 작성되므로 타입들의 설명은 공식 문서를 참고해 주세요.)
2번 방법은 Regex로 바로 만드는 방법입니다.
1번과 3번 방법은 String이기 때문에 컴파일 타임에 정규표현식에 문제가 있는지 없는지 알 수 없습니다.
하지만 2번 방법은 Xcode에서 컴파일 에러로 정규표현식이 유효한지 알려줍니다.
또한, pattern1과 pattern3의 차이는 꽤나 중요합니다.
Swift에서 정규표현식을 만들 때 \ 는 \\로 작성해야하는데요.
Swift의 \는 이미 이스케이프((Escape delimiter)문자로 지정되어 있기 때문입니다.
예를 들어, \s 를 작성하고 싶다면 \\s로 작성해야 하는겁니다.
그러다보니 Swift만 다른 언어와 정규표현식 패턴이 달라지는 문제가 생기는데요.
(바로 아래에서 소개 드릴 정규표현식 해석 사이트에서도 제대로 해석이 안 됩니다 ㅠ)
이를 해결해주는게 pattern3 방법입니다.
문자열 양 옆에 #을 추가하면 String의 Raw String을 얻을 수 있기 때문에 \을 그대로 사용할 수 있습니다.
정규표현식 참고 사이트
정규표현식을 만들어볼 수 있는 사이트입니다.
아래는 정규표현식을 시각화 해주는 사이트입니다.
예제로 알아보자
이제 예제를 살펴보면서 정규표현식과 익숙해져 봅시다.
정규표현식 검증은 위 사이트 중 Regexper에서 해보겠습니다.
한글 찾기
문자열에서 한글을 찾는 정규표현식을 생각해봅시다.
let pt1 = #"[가-힣ㄱ-ㅎㅏ-ㅣ]"#
한글은 가~힣, ㄱ~ㅎ, ㅏ~ㅣ세 가지 범위로 구성됩니다.
위 범위를 집합으로 구성해야 하므로 [ ]로 범위를 감싸면 되겠습니다.
영어 찾기
영어는 한글보다 쉽습니다.
a-z, A-Z를 범위로 지정하면 되겠죠.
let pt2 = #"[a-zA-Z]"#
한글 제외하기
이젠 제외 시키는 패턴을 만들어 봅시다.
문자열에서 한글을 지우려면 ^ 로 한글을 부정(?)하면 됩니다.
let pt3 = #"[^가-힣ㄱ-ㅎㅏ-ㅣ]"#
한글 자음, 모음 제외하기
그럼 한글에서 완성된 글자만 포함하고 싶다면 어떻게 하면 될까요?
처음에는 ㄱ-ㅎ, ㅏ-ㅣ를 제외해야 하나? 했는데 ㅋㅋ
굉장히 멍청한 생각이었구여;;
let pt4 = #"[가-힣]"#
그냥 가-힣만 범위로 지정하면 되겠습니다.
전화번호 패턴
핸드폰 번호가 유효한지 체크하는 정규표현식을 알아봅시다.
다양한 케이스가 있겠지만 가장 간단한 방법은 숫자 3글자, 숫자 3~4글자, 숫자 4글자의 조합일 것입니다.
let pt5 = #"^\d{3}-\d{3,4}-\d{4}$"#
전화번호의 패턴은 시작과 끝이 중요하므로 ^ $를 사용하였습니다.
이제 조건을 추가하여 01로 시작하는 케이스를 표현해 봅시다.
let pt6 = #"^01[0-1]{1}-\d{3,4}-\d{4}$"#
01로 시작하고 01 뒤에 0 또는 1이 1글자 들어가는 패턴을 표현했습니다.
또 조건을 추가해봅시다.
이제는 - 가 있을 수도 있고, 없을 수도 있는 케이스를 표현해봅시다.
let pt7 = #"^01[0-1]{1}-?\d{3,4}-?\d{4}$"#
- 뒤에 ? 를 붙여서 0개 또는 1개가 들어갈 수 있는 패턴을 표현했습니다.
이메일 패턴
다음은 이메일 패턴입니다.
이메일은 중간에 @가 들어가야 하고, 그 뒤에 . 이 들어가야 합니다.
let pt8 = #"[A-Z0-9a-z]+@[A-Za-z0-9]+\.[A-Za-z]+"#
[A-Z0-9a-z]에 _를 추가로 붙인 패턴은 [A-Z0-9a-z_]로 표현할 수 있고, 이건 다시 \w로 표현할 수 있습니다.
let pt9 = #"\w+@\w+\.[A-Za-z]+"#
특정 문자 n번 연속 패턴
특정 문자가 n번 연속 반복되는지 확인하는 패턴입니다.
여기서 특정 문자를 직접 하드코딩할 수도 있지만 . 을 사용할 수도 있습니다.
let pt10 = #"(.)\1"#
특정 문자가 2번 반복하는 패턴입니다.
더 많은 횟수로 반복하길 원한다면 \1을 여러 번 쓰면 됩니다.
let pt10 = #"(.)\1"# //2번 반복
let pt11 = #"(.)\1\1"# //3번 반복
let pt12 = #"(.)\1\1\1"# //4번 반복
var, let 판단하기
이제 좀 특이한 예제를 봐볼까요?
var a: Int = 10
let b: Int = 20
위 코드를 입력했을 때 var인지 let인지 판단하는 정규표현식을 만들어 봅시다.
먼저 = 기호 전 까지만 잘라서 표현해 봅시다.
let pt13 = #"^var\s+(\w+)\s*:\s*(\w+)$"#
먼저 var로 시작을 하고 var와 문자열 사이에 space가 1개 이상 존재합니다.
문자열과 : 사이에는 space가 0개 이상 존재합니다.
:과 문자열 사이도 space가 0개 이상 존재하도록 표현했습니다.
이어서 = 까지 표현해봅시다.
Int형으로 가정하고 정수값이 대입되는 걸 표현해 볼게요.
let pt14 = #"^var\s+(\w+)\s*:\s*Int\s*=\s*(\d+)$"#
var + 1개 이상 공백 + 단어 + 0개 이상 공백 + : + 0개 이상 공백 + Int + 0개 이상 공백 + = + 0개 이상 공백 + 숫자 입니다.
굉장히 복잡해 보이지만 천천히 하나씩 뜯어보니 별거 없죠?
날짜
마지막으로 날짜를 표현하는 정규표현식을 알아봅시다.
let pt15 = #"19[0-9]{2}|20[0-9]{2}"# //연도
let pt16 = #"0[1-9]|1[0-2]"# //월
let pt17 = #"0[1-9]|1[0-9]|2[0-9]|3[0-1]"# //일
연도는 19 또는 20 이후에 0~9까지의 숫자 두 개가 들어갈 수 있습니다.
월은 0 또는 1로 시작하고 뒤에 숫자 하나가 들어갑니다.
일은 0, 1, 2, 3 뒤에 숫자가 하나 들어갑니다.
마무리
솔직히 쉽지 않습니다.
언제쯤 쉬워질지 모르겠습니다.
그래도 정규표현식을 사용한다면 문자열 처리가 확실히 짧아지고 센스있게 변합니다.
꾸준히 연습해서 익숙해지도록 노력해야겠습니다.
2편: 정규표현식 사용해보기
감사합니다.
참고
https://borabong.tistory.com/32
https://ios-development.tistory.com/1087
https://ios-development.tistory.com/277
https://green1229.tistory.com/280
https://tngusmiso.tistory.com/62
아직은 초보 개발자입니다.
더 효율적인 코드 훈수 환영합니다!
공감과 댓글 부탁드립니다.