정규식 패턴과 일치하는 문자열에서 하위 문자열을 추출하고 싶습니다.
그래서 나는 이와 같은 것을 찾고 있습니다 :
func matchesForRegexInText(regex: String!, text: String!) -> [String] {
???
}
이것이 내가 가진 것입니다 :
func matchesForRegexInText(regex: String!, text: String!) -> [String] {
var regex = NSRegularExpression(pattern: regex,
options: nil, error: nil)
var results = regex.matchesInString(text,
options: nil, range: NSMakeRange(0, countElements(text)))
as Array<NSTextCheckingResult>
/// ???
return ...
}
문제는 유형이 어디 matchesInString
의 배열 을 제공 한다는 것입니다 .NSTextCheckingResult
NSTextCheckingResult.range
NSRange
NSRange
와 호환되지 않으므로 Range<String.Index>
사용하지 못합니다.text.substringWithRange(...)
너무 많은 코드 줄 없이이 간단한 일을 신속하게 달성하는 방법에 대한 아이디어가 있습니까?
답변
matchesInString()
메소드가 String
첫 번째 인수로 a 를 사용 하더라도 내부적으로로 작동 NSString
하며 range 매개 변수는 NSString
Swift 문자열 길이가 아닌 길이를 사용하여 제공되어야합니다 . 그렇지 않으면 “플래그”와 같은 “확장 grapheme 클러스터”에 대해 실패합니다.
현재 스위프트 4 (엑스 코드 9), 스위프트 표준 라이브러리 사이의 변환을 제공 기능 Range<String.Index>
과 NSRange
.
func matches(for regex: String, in text: String) -> [String] {
do {
let regex = try NSRegularExpression(pattern: regex)
let results = regex.matches(in: text,
range: NSRange(text.startIndex..., in: text))
return results.map {
String(text[Range($0.range, in: text)!])
}
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
예:
let string = "??€4€9"
let matched = matches(for: "[0-9]", in: string)
print(matched)
// ["4", "9"]
참고 : 지정된 줄의 하위 문자열을 참조 Range($0.range, in: text)!
하므로 강제 줄 바꿈 이 안전 NSRange
합니다 text
. 그러나 피하고 싶다면 다음을 사용하십시오.
return results.flatMap {
Range($0.range, in: text).map { String(text[$0]) }
}
대신에.
(Swift 3 및 이전 버전의 이전 답변 🙂
주어진 Swift 문자열을로 변환 NSString
한 다음 범위를 추출해야합니다. 결과는 Swift 문자열 배열로 자동 변환됩니다.
Swift 1.2의 코드는 편집 기록에서 찾을 수 있습니다.
스위프트 2 (Xcode 7.3.1) :
func matchesForRegexInText(regex: String, text: String) -> [String] {
do {
let regex = try NSRegularExpression(pattern: regex, options: [])
let nsString = text as NSString
let results = regex.matchesInString(text,
options: [], range: NSMakeRange(0, nsString.length))
return results.map { nsString.substringWithRange($0.range)}
} catch let error as NSError {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
예:
let string = "??€4€9"
let matches = matchesForRegexInText("[0-9]", text: string)
print(matches)
// ["4", "9"]
스위프트 3 (Xcode 8)
func matches(for regex: String, in text: String) -> [String] {
do {
let regex = try NSRegularExpression(pattern: regex)
let nsString = text as NSString
let results = regex.matches(in: text, range: NSRange(location: 0, length: nsString.length))
return results.map { nsString.substring(with: $0.range)}
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
예:
let string = "??€4€9"
let matched = matches(for: "[0-9]", in: string)
print(matched)
// ["4", "9"]
답변
내 답변은 주어진 답변을 기반으로하지만 추가 지원을 추가하여 정규식 일치를보다 강력하게 만듭니다.
- 일치하는 항목 만 반환 할뿐만 아니라 각 일치하는 모든 캡처 그룹도 반환합니다 (아래 예 참조).
- 이 솔루션 은 빈 배열을 반환하는 대신 선택적 일치를 지원합니다.
- 을 피
do/catch
콘솔에 인쇄하지 않음으로써 및 차종은의 사용guard
구조 - 추가
matchingStrings
로 까지 확장String
스위프트 4.2
//: Playground - noun: a place where people can play
import Foundation
extension String {
func matchingStrings(regex: String) -> [[String]] {
guard let regex = try? NSRegularExpression(pattern: regex, options: []) else { return [] }
let nsString = self as NSString
let results = regex.matches(in: self, options: [], range: NSMakeRange(0, nsString.length))
return results.map { result in
(0..<result.numberOfRanges).map {
result.range(at: $0).location != NSNotFound
? nsString.substring(with: result.range(at: $0))
: ""
}
}
}
}
"prefix12 aaa3 prefix45".matchingStrings(regex: "fix([0-9])([0-9])")
// Prints: [["fix12", "1", "2"], ["fix45", "4", "5"]]
"prefix12".matchingStrings(regex: "(?:prefix)?([0-9]+)")
// Prints: [["prefix12", "12"]]
"12".matchingStrings(regex: "(?:prefix)?([0-9]+)")
// Prints: [["12", "12"]], other answers return an empty array here
// Safely accessing the capture of the first match (if any):
let number = "prefix12suffix".matchingStrings(regex: "fix([0-9]+)su").first?[1]
// Prints: Optional("12")
스위프트 3
//: Playground - noun: a place where people can play
import Foundation
extension String {
func matchingStrings(regex: String) -> [[String]] {
guard let regex = try? NSRegularExpression(pattern: regex, options: []) else { return [] }
let nsString = self as NSString
let results = regex.matches(in: self, options: [], range: NSMakeRange(0, nsString.length))
return results.map { result in
(0..<result.numberOfRanges).map {
result.rangeAt($0).location != NSNotFound
? nsString.substring(with: result.rangeAt($0))
: ""
}
}
}
}
"prefix12 aaa3 prefix45".matchingStrings(regex: "fix([0-9])([0-9])")
// Prints: [["fix12", "1", "2"], ["fix45", "4", "5"]]
"prefix12".matchingStrings(regex: "(?:prefix)?([0-9]+)")
// Prints: [["prefix12", "12"]]
"12".matchingStrings(regex: "(?:prefix)?([0-9]+)")
// Prints: [["12", "12"]], other answers return an empty array here
// Safely accessing the capture of the first match (if any):
let number = "prefix12suffix".matchingStrings(regex: "fix([0-9]+)su").first?[1]
// Prints: Optional("12")
스위프트 2
extension String {
func matchingStrings(regex: String) -> [[String]] {
guard let regex = try? NSRegularExpression(pattern: regex, options: []) else { return [] }
let nsString = self as NSString
let results = regex.matchesInString(self, options: [], range: NSMakeRange(0, nsString.length))
return results.map { result in
(0..<result.numberOfRanges).map {
result.rangeAtIndex($0).location != NSNotFound
? nsString.substringWithRange(result.rangeAtIndex($0))
: ""
}
}
}
}
답변
위치뿐만 아니라 (이모지를 포함한 실제 문자열) 문자열에서 하위 문자열을 추출하려는 경우. 그러면 다음이 더 간단한 해결책 일 수 있습니다.
extension String {
func regex (pattern: String) -> [String] {
do {
let regex = try NSRegularExpression(pattern: pattern, options: NSRegularExpressionOptions(rawValue: 0))
let nsstr = self as NSString
let all = NSRange(location: 0, length: nsstr.length)
var matches : [String] = [String]()
regex.enumerateMatchesInString(self, options: NSMatchingOptions(rawValue: 0), range: all) {
(result : NSTextCheckingResult?, _, _) in
if let r = result {
let result = nsstr.substringWithRange(r.range) as String
matches.append(result)
}
}
return matches
} catch {
return [String]()
}
}
}
사용법 예 :
"someText ???⚽️ pig".regex("?⚽️")
다음을 반환합니다 :
["?⚽️"]
“\ w +”를 사용하면 예기치 않은 “”이 생성 될 수 있습니다.
"someText ???⚽️ pig".regex("\\w+")
이 문자열 배열을 반환합니다
["someText", "️", "pig"]
답변
안타깝게도 받아 들여진 대답의 솔루션이 Linux 용 Swift 3에서 컴파일되지 않는다는 것을 알았습니다. 수정 된 버전은 다음과 같습니다.
import Foundation
func matches(for regex: String, in text: String) -> [String] {
do {
let regex = try RegularExpression(pattern: regex, options: [])
let nsString = NSString(string: text)
let results = regex.matches(in: text, options: [], range: NSRange(location: 0, length: nsString.length))
return results.map { nsString.substring(with: $0.range) }
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
주요 차이점은 다음과 같습니다.
-
Linux의 Swift
NS
는 Swift 네이티브가 아닌 Foundation 객체 에서 접두사를 삭제해야 합니다. ( Swift evolution 제안 # 86 참조 ) -
Linux의 Swift
options
에는RegularExpression
초기화 및matches
메소드 모두에 대한 인수를 지정해야합니다 . -
어떤 이유로 든 Linux의 Swift에서는 a
String
로 강제 변환이NSString
작동하지 않지만 소스가 작동하면NSString
aString
로 새 항목 을 초기화합니다 .
이 버전은 macOS / Xcode의 Swift 3에서도 작동합니다. 단, 이름을 NSRegularExpression
대신 사용해야합니다 RegularExpression
.
답변
@ p4bloch 일련의 캡처 괄호에서 결과를 캡처하려면 대신 대신의 rangeAtIndex(index)
방법 을 사용해야합니다 . 위의 Swift2에 대한 @MartinR의 방법은 캡처 괄호에 적합합니다. 반환되는 배열에서 첫 번째 결과 는 전체 캡처이며 개별 캡처 그룹은에서 시작됩니다 . 작업에 주석을 달았 으므로 변경 한 내용을 쉽게 볼 수 있으며 중첩 루프로 대체했습니다.NSTextCheckingResult
range
[0]
[1]
map
func matches(for regex: String!, in text: String!) -> [String] {
do {
let regex = try NSRegularExpression(pattern: regex, options: [])
let nsString = text as NSString
let results = regex.matchesInString(text, options: [], range: NSMakeRange(0, nsString.length))
var match = [String]()
for result in results {
for i in 0..<result.numberOfRanges {
match.append(nsString.substringWithRange( result.rangeAtIndex(i) ))
}
}
return match
//return results.map { nsString.substringWithRange( $0.range )} //rangeAtIndex(0)
} catch let error as NSError {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
사용 사례의 title year
예로 “Finding Dory 2016” 과 같은 문자열을 분할하고 싶다고 할 수 있습니다.
print ( matches(for: "^(.+)\\s(\\d{4})" , in: "Finding Dory 2016"))
// ["Finding Dory 2016", "Finding Dory", "2016"]
답변
NSString이없는 스위프트 4
extension String {
func matches(regex: String) -> [String] {
guard let regex = try? NSRegularExpression(pattern: regex, options: [.caseInsensitive]) else { return [] }
let matches = regex.matches(in: self, options: [], range: NSMakeRange(0, self.count))
return matches.map { match in
return String(self[Range(match.range, in: self)!])
}
}
}
답변
위의 대부분의 솔루션은 캡처 그룹을 무시한 결과로 전체 일치 만 제공합니다. 예 : ^ \ d + \ s + (\ d +)
캡처 그룹이 예상대로 일치하도록하려면 (Swift4)와 같은 것이 필요합니다.
public extension String {
public func capturedGroups(withRegex pattern: String) -> [String] {
var results = [String]()
var regex: NSRegularExpression
do {
regex = try NSRegularExpression(pattern: pattern, options: [])
} catch {
return results
}
let matches = regex.matches(in: self, options: [], range: NSRange(location:0, length: self.count))
guard let match = matches.first else { return results }
let lastRangeIndex = match.numberOfRanges - 1
guard lastRangeIndex >= 1 else { return results }
for i in 1...lastRangeIndex {
let capturedGroupIndex = match.range(at: i)
let matchedString = (self as NSString).substring(with: capturedGroupIndex)
results.append(matchedString)
}
return results
}
}
