정규표현식
정규 표현식(regular expression), 간단히 regexp 또는 regex, rational expression) 또는 정규식 (正規式)은 특정한 규칙을 가진 문자열의 집합을 표현하는 데 사용하는 형식 언어 이다. - 위키백과
특정한 규칙을 가진 문자열의 집합을 표현하는데 사용하는 형식 언어.
기본 문법
^ | 시작 | \d | 숫자 |
$ | 종료 | \D | 숫자를 제외한 모든 문자 |
{N, } | N개 이상 | ? | 없거나 하나이상 |
[ ] | 괄호 안의 임의의 문자 | ||
( ) | 문자 묶음 처리 | ||
(.) | 임의의 문자 | ||
\1 | 앞의 부분식 | ||
* | 0개 이상 == {0, } | ||
+ | 1개 이상 == {1, } | ||
? | 1개 == {0, 1} | ||
\W | 알파벳 or 숫자를 제외한 문자 |
||
\w | 알파벳 or 숫자 |
예제
let pattern = "^[A-Za-z0-9]{0,}$" //A-Z,a-z,0~9까지 숫자중 하나가 포함되어 있는지 확인하는 패턴
// ^: 시작
// $: 종료
// {0,}: 0개 이상
// []: 괄호 안에 있는 문자중 하나
//핸드폰 번호 정규식
let pattern = "^01([0-9])([0-9]){3,4}([0-9]{4}$" // 01 다음 0~9중 하나, 0~9중 3개이상 4개이하, 0~9중 4개
let pattern = "(.)\\1{3,}"
// 앞과 동일한 문자가 있어야하고 그길이는 3이상이여야함
// 앞문자 + 동일한문자3글자이상 -> 중복문자 4글자이상
주의: ' \ ' 를 인식하기 위해서는 ' \\ ' 두번 써줘야함!
다른 모든표현에도 ' \ '가 들어가있는 연산자라면 ' \ ' 를 하나더 붙여서 ' \\' 형식으로 사용해야함.
// 이메일 정규식 ex) seob@JJangJJangMan.com
let pattern = "^[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,20}$"
// 해석
// ^[A-Z0-9a-z._%+-]: A~Z,0~9,a~z,.,_,%,+,- 문자열 중 한 문자가
// +: 하나이상 있어야 되. == {1,}
// @: 사이에 골뱅이 추가 해주고
// [A-Za-z0-9.-]: A~Z,0~9,a~z,.,- 문자열 중 한 문자가
// +: 하나이상 있어야되.
// \\.: .추가하고 ('.'자체를 문자로 인식하기 위해서 \. -> \\를 같이 써줌. )
// [A-Za-z]{2,20}$: A-Z,a-z중 하나를 포함한 게 총 2~ 20자
정규식 패턴 연습 사이트: https://regexr.com/
연습사이트에서는 $를 빼야 Test가 정확히 되던데 아직 이유는 모르겠다..
이후 예제를 다뤄보겠지만 Swift문법이랑 정확히 일치하지는 않는 것 처럼 보였다.
주요 사용처
이메일, 핸드폰, 회원가입시 입력, 코테? 등등.. 특정한 형식이 정해져 있는 곳 입력값을 검증하는데 주로 사용.
Swift 문법
Swift에서 정규식을 사용하기 위한 방법은 2가지가 있음.
1. NSPredicate
직역 with Papago: 가져오기 또는 메모리 내 필터링에 대한 검색을 제한하기 위한 논리적 조건의 정의.
NSArray 혹은 CoreData를 필터링 할 때 주로 사용된다고 한다. 더 자세한내용 Predicate Programming Guide
NSPredicate의 evaluate 함수를 이용
input에 따라 Bool값을 리턴.
let regex = "^01[0-1, 7][0-9]{7,8}$"
let phonePredicate = NSPredicate(format: "SELF MATCHES %@", regex)
let isValid = phonePredicate.evaluate(with: input)
2. NSRegularExpression
직역 with Papago: 유니코드 문자열에 적용하는 컴파일된 정규식의 불변 표현.
이름에서 부터 Regular Expression이며, 윗 방식보다 개인적으로 편하다고 생각함.
String타입의 range함수 이용
처음으로 일치부분의 범위를 리턴
만약 regex와 일치하는 부분이 있다면 첫번째 일치부분의 Range<index>가 옵셔널로 나옴.
// string: 검사하고픈 문자열
// regex: 정규표현식 패턴
string.range(of: regex, options: .regularExpression)
예제.
let strings: [String] = ["abc", "123", "ab12cd34"]
let pattern: String = "[0-9]*"
for string in strings {
guard let range = string.range(of: pattern, options: .regularExpression) else {
print("\(string)에는 숫자가 없습니다.")
continue
}
print("\(string)에서 숫자로 이루어 진 부분은 \(string[range]) 입니다.") // 가장 앞에 일치하는 부분만 확인 가능
}
1. abc 에는 숫자가 없습니다
2. 123에서 숫자로 이루어 진 부분은 123입니다.
3. ab12cd34에서 숫자로 이루어 진 부분은 12입니다.
라고 예상을 했으나..
???
분명 테스트 사이트에서는 되는데..
삽질주의🥲
조건: 0개이상 조건(*)을 뺴보자
결과: 한개만 나옴.
조건: ^(시작)과 $(끝)를 붙여보자
결과: (처음부터 숫자가 나오는) 두번째만 성공
조건: 끝($)에만 줘보자
결과: 두번째, 세번째 성공으로 보이지만.. 세번쨰 String은 뒷자리가 34로 숫자로 시작하기 때문에 인식이 성공된 것 같다.
추측: 이유는 잘모르겠지만 Swift에서는 조건식([0-9]) 다음에 0개이상 조건(*)을 주면 안되는 경우가 있는건가??
조건: 다른 조건(1개이상 ' +' ) 주입
결과: 성공!
문자열 전체가 일치하는지 검사
시작(^) 과 끝($)를 붙이자.
let strings: [String] = ["abc", "123", "ab12cd34"]
let pattern: String = "^[0-9]+[0-9]$"
for string in strings {
guard let range = string.range(of: pattern, options: .regularExpression) else {
print("\(string)에는 숫자가 없습니다.")
continue
}
print("\(string)에서 숫자로 이루어 진 부분은 \(string[range]) 입니다.") // 가장 앞에 일치하는 부분만 확인 가능
}
Regular Expression자체를 만들어서 활용 예제
RegularExpression을 활용하려면 Error처리를 해주어야 한다.
do {
// pattern == 정규 표현식으로 쓸 String
// pattern을 이용 NSRegularExpression 생성
let regex = try NSRegularExpression(pattern: pattern, options: [])
//일치하는 부분 찾기(match함수 이용, firstMatch를 이용하면 첫번째 일치항목 가지고 오기 가능.)
let result = regex.matches(in: string, options: [], range: NSRange(location: 0, length: string.count))
// result에서 결과값을 subscript해서 만들기
let rexStrings = result.map { (element) -> String in
let range = Range(element.range, in: string)!
return String(string[range])
}
print(rexStrings)
} catch let error {
print(error.localizedDescription)
}
조건 1
조건 2
조건 3
조건 4
정리
방법 | Predicate(evaluate) | string.range | RegularExpression(match) |
리턴값 | Bool | NSRange | NSTextCheckingResult |
특징 | - Data를 필터링 할때 자주 사용 - format등 익숙해지기 쉽지 않음 |
- 제일 간단함 - 처음으로 일치하는 범위만 가지고 오기 가능 |
- 일치하는 모든 범위 가지고 오기 가능 - 구현하기 쉽지 않음 |
결론
이메일등의 유효성 검사를 할것이라면 string.range의 pattern에 ^$를 붙여서 해결하는 것이 내겐 가장 간단해 보인다.하지만, 일치하는 모든 범위를 다 알고싶다면 RegularExpression를 활용해보자.
잘못 된 부분이나 개선할 부분에 대한 피드백은 언제나 환영입니다 🤗
참고 및 예제 코드 출처
https://tngusmiso.tistory.com/62 NSReqularExpression 예제
https://nsios.tistory.com/139 정규식 패턴 Swift 예제
https://onelife2live.tistory.com/35 NSPredicate 문법 정리
https://ios-development.tistory.com/592 NSPredicate 예제
https://highcode.tistory.com/6 자주 쓰이는 정규표현식 및 정규표현식 문법
https://zeddios.tistory.com/483 NSString compare Option
https://beagle-dev.tistory.com/114 비밀번호 정규식 모음
'Swift' 카테고리의 다른 글
클래스의 초기화 (0) | 2022.08.07 |
---|---|
DiffableDataSource 알아보기. (7) | 2022.07.25 |
Type Erasure in Swift (0) | 2022.05.23 |
Data Testing in Swift (0) | 2022.05.12 |
Generic을 이용한 CompileTime에 Type 검사하기. (0) | 2022.05.12 |
댓글