본문 바로가기
Swift

정규 표현식 in Swift

by SeoB-P 2022. 6. 14.

정규표현식

정규 표현식(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

댓글