Notice
Recent Posts
Recent Comments
Link
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
반응형
Archives
Today
Total
관리 메뉴

슈프림 블로그

[Swift] 코딩테스트 보다가 열 받아서 정리하는 Swift 정규식 - NSRegularExpression (Regex) 본문

iOS_Swift

[Swift] 코딩테스트 보다가 열 받아서 정리하는 Swift 정규식 - NSRegularExpression (Regex)

_슈프림 2021. 3. 21. 20:27
728x90

문자열 문제 진짜 어렵다!!!!!!!!!

매번 구글링 하지 말고 정리해 둬야 겠다는 필요성을 느꼈다...

 

Swift 주의사항

문자열에서 역슬래쉬(\)는 연산자 역할을 하므로, \를 문자 자체로 사용하고 싶은 경우에는 "\\" 처럼 두 번 사용해야 한다.

=> 이거,, 매번 눈 빠지게 1개씩 썼는지 2개씩 썼는지 확인하지 말고 자동 변환해주는 프로그램을 하나 만들까....?ㅠㅠ

 

Swift 정규식 사용 방법

🐶 문자열 일부가 일치하는지 확인하는 법 (첫번째로 일치하는 부분)

이 방법은 문자열에서 정규식과 일치하는 모든 부분을 구할 수는 없다.

가장 첫번째로 일치하는 부분만 구할 수 있기 때문이다.

 

NSRegularExpression.firstMatch 를 사용하기도 하지만, 나는 아래 방법이 더 편리한 것 같다.

// string: 정규식과 비교할 문자열
// regex: 정규식 문자열

string.range(of: regex, options: .regularExpression)

stringregex와 일치하는 부분이 있다면 가장 첫번째로 일치하는 부분의 Range<Index> 값이 나온다.

일치하는 부분이 없다면 결과가 nil이 나온다.

Optional이기 때문에 언랩핑하여 string[range]와 같이 활용할 수 있다.

 

예시

문자열에 숫자가 포함되어있는지 확인하는 방법은 다음과 같다.

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]) 입니다.") // 가장 앞에 일치하는 부분만 확인 가능
}

 

abc에는 숫자가 없습니다.
123에서 숫자로 이루어 진 부분은 123입니다.
ab12cd34에서 숫자로 이루어 진 부분은 12입니다.

 

 

🐶 문자열 전체가 일치하는지 확인하는 법

위 방법과 동일하지만, 정규식 앞, 뒤에 ^$를 붙여주면 된다.

let strings: [String] = ["abc", "123", "ab12cd34"]
let pattern: String = "^[0-9]*$"

for string in strings {
    guard let _ = string.range(of: pattern, options: .regularExpression) else {
        print("\(string)은 숫자로만 이루어지지 않았습니다.")
        continue
    }
    print("\(string)은 숫자로만 이루어져 있습니다.")
}

 

abc은 숫자로만 이루어지지 않았습니다.
123은 숫자로만 이루어져 있습니다.
ab12cd34은 숫자로만 이루어지지 않았습니다.

 

 

🐶 일치하는 모든 범위를 알아야 하는 경우

NSRegularExpression을 사용할 때는 try? 또는 do try catch 문을 사용해주어야 한다.

do {
    let regex = try NSRegularExpression(pattern: pattern, options: [])
    let result = regex.matches(in: string, options: [], range: NSRange(location: 0, length: string.count))
    
    let rexStrings = result.map { (element) -> String in
        let range = Range(element.range, in: string)!
        return String(string[range])
    }
    return rexStrings
} catch let error {
    print(error.localizedDescription)
}

pattern: 정규식으로 사용할 문자열

regex: pattern을 정규식으로 변환

result: string에서 regex와 일치하는 부분들 (배열) -> map을 사용하여 Range<Index>, 또는 String 배열로 변환하여 활용할 수 있다.

 

+ matches() 대신 firstMatch()를 사용하면 가장 첫번째로 일치하는 부분만 구해낼 수도 있다.

 

 

🐶 그룹으로 묶인 문자열에 이름을 붙여줄 수도 있다.

정규식을 통해 찾고 싶은 문자열에 대한 이름을 붙여주고, 그 이름으로 찾는 방법이다!

그룹이란, 괄호를 통해 묶인 부분을 말한다. 그룹에는 이름을 붙여줄 수 있다.

괄호 안의 처음에 ?<name>과 같은 형태로 적어주면 된다.

let pattern: String = "(?<name>......)"

 

pattern과 일치한 부분 중 이름이 지정된 부분을 찾고 싶으면 NSTextCheckingResult.range(withName:)을 사용한다.

result.range(withName: "name")

예시

문자열에서 yyyy.mm.dd의 날짜 형식을 모두 찾아내고

각 찾아낸 결과값에서 year, month, date를 찾는 방법!

 

let string: String = "기간은 2021.03.01 부터 2021.03.31까지 입니다."
let datePattern: String = "(?<year>(19|20)[0-9]{2})\\.(?<month>(0[1-9]|1[0-2]))\\.(?<date>(0[1-9]|1[0-9]|2[0-9]|3[0-1]))"

let regex = try? NSRegularExpression(pattern: datePattern, options: [])
if let result = regex?.matches(in: string, options: [], range: NSRange(location: 0, length: string.count)) {
    let rexStrings = result.map { (element) -> String in
        let yearRange = Range(element.range(withName: "year"), in: string)!
        let monthRange = Range(element.range(withName: "month"), in: string)!
        let dateRange = Range(element.range(withName: "date"), in: string)!
    
        return "\(string[yearRange])년 \(string[monthRange])월 \(string[dateRange])일"
    }
    print(rexStrings)
}

 

["2021년 03월 01일", "2021년 03월 31일"]

 

 

자주 쓰는 정규식

대문자로만 구성

"^[A-Z]*$"

소문자로만 구성

"^[a-z]*$"

숫자로만 구성

"^[0-9]*$"
"^[\\d]*$"

// 반대 (숫자 제외)
"^[\\D]*$"

알파벳 + 숫자로만 구성

"^[0-9a-zA-Z]*$"
"^[\\w]*$"

// 반대 (알파벳, 숫자 제외)
"^[\\W]*$"

응용))) 숫자, 알파벳, 특수문자 (~!@#$%^&*) 로만 구성

"^[0-9a-zA-Z~!@#\\$%\\^&\\*]*$"
"^[\\w~!@#\\$%\\^&\\*]*$"

최소 하나 이상의 something

"(?=.*[something])"

응용) 최소 하나 이상의 대문자, 하나 이상의 소문자, 하나 이상의 숫자

"(?=.*[A-Z])(?=.*[a-z])(?=.*[\\d])"

응용2) 최소 하나 이상의 대문자, 하나 이상의 소문자, 하나 이상의 숫자, 하나 이상의 특수문자(~!@#$%^&*), 그 외 불포함

"^(?=.*[A-Z])(?=.*[a-z])(?=.*[\\d])(?=.*[~!@#\\$%\\^&\\*])[\\w~!@#\\$%\\^&\\*]{8,}$"

 


같은 문자가 n번 연속되는 경우

 

. 어떤 문자든지 한글자를 의미한다.
() 괄호 사이에 정규식 연산이 들어갈 수 있는데, 이 연산으로 매칭 된 값을 캡쳐한다.
([A-Z]+)는 알파벳 대문자 여러 글자와 매칭되고, ex) "APPLE" 전체 문자열과 매칭된다.
([A-Z])+는 알파벳 대문자 중 한글자가 여러번 반복되는 형태와 매칭된다. ex) "APPLE" 에서 "PP"와 매칭된다.
\1 재참조 메타 문자 : 첫번째 그룹(괄호로 묶인 부분) 형태가 1번 반복된다.

 

Swift에서 \는 2번 사용해야함을 잊지 말자

이것들을 조합해보면??

"(.)\\1"

알파벳, 숫자, 특수문자 상관 없이 동일한 한글자가 2번 반복되는 형태를 찾을 수 있다.

더 많은 횟수로 반복하길 원한다면, \1을 여러번 써준다.

"(.)\\1"		// 2번 반복
"(.)\\1\\1"		// 3번 반복
"(.)\\1\\1\\1"		// 4번 반복
"(.)\\1\\1\\1\\1"	// 5번 반복

모든 문자가 아닌, 특정 문자라면 괄호 안에 특정 문자를 넣어주면 된다.

 

반응형
Comments