Tôi muốn trích xuất chuỗi con từ một chuỗi khớp với mẫu regex.
Vì vậy, tôi đang tìm kiếm một cái gì đó như thế này:
func matchesForRegexInText(regex: String!, text: String!) -> [String] {
???
}
Vì vậy, đây là những gì tôi có:
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 ...
}
Vấn đề là, nó matchesInString
cung cấp cho tôi một mảng NSTextCheckingResult
, đâu NSTextCheckingResult.range
là loại NSRange
.
NSRange
không tương thích với Range<String.Index>
, vì vậy nó ngăn tôi sử dụngtext.substringWithRange(...)
Bất kỳ ý tưởng làm thế nào để đạt được điều đơn giản này nhanh chóng mà không cần quá nhiều dòng mã?
Ngay cả khi matchesInString()
phương thức lấy a String
làm đối số đầu tiên, nó hoạt động bên trong NSString
và tham số phạm vi phải được cung cấp bằng NSString
độ dài chứ không phải bằng độ dài chuỗi Swift. Nếu không, nó sẽ không thành công đối với "cụm grapheme mở rộng" chẳng hạn như "cờ".
Kể từ Swift 4 (Xcode 9), thư viện chuẩn Swift cung cấp các hàm để chuyển đổi giữa Range<String.Index>
và 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 []
}
}
Thí dụ:
let string = "🇩🇪€4€9"
let matched = matches(for: "[0-9]", in: string)
print(matched)
// ["4", "9"]
Lưu ý: Việc mở buộc bắt buộc Range($0.range, in: text)!
là an toàn vì tham chiếu NSRange
đến một chuỗi con của chuỗi đã cho text
. Tuy nhiên, nếu bạn muốn tránh nó thì hãy sử dụng
return results.flatMap {
Range($0.range, in: text).map { String(text[$0]) }
}
thay thế.
(Câu trả lời cũ hơn cho Swift 3 trở về trước :)
Vì vậy, bạn nên chuyển đổi chuỗi Swift đã cho thành an NSString
và sau đó trích xuất các phạm vi. Kết quả sẽ được tự động chuyển thành mảng chuỗi Swift.
(Có thể tìm thấy mã cho Swift 1.2 trong lịch sử chỉnh sửa.)
Swift 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 []
}
}
Thí dụ:
let string = "🇩🇪€4€9"
let matches = matchesForRegexInText("[0-9]", text: string)
print(matches)
// ["4", "9"]
Swift 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 []
}
}
Thí dụ:
let string = "🇩🇪€4€9"
let matched = matches(for: "[0-9]", in: string)
print(matched)
// ["4", "9"]
Câu trả lời của tôi được xây dựng dựa trên các câu trả lời đã cho nhưng làm cho kết hợp regex mạnh mẽ hơn bằng cách thêm hỗ trợ bổ sung:
do/catch
bằng cách không in ra bảng điều khiển và sử dụng guard
cấu trúcmatchingStrings
dưới dạng phần mở rộng vàoString
Swift 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")
Swift 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")
Swift 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))
: ""
}
}
}
}
Nếu bạn muốn trích xuất chuỗi con từ một Chuỗi, không chỉ vị trí, (mà là Chuỗi thực tế bao gồm biểu tượng cảm xúc). Sau đó, sau đây có thể là một giải pháp đơn giản hơn.
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]()
}
}
}
Cách sử dụng ví dụ:
"someText 👿🏅👿⚽️ pig".regex("👿⚽️")
Sẽ trả lại như sau:
["👿⚽️"]
Lưu ý sử dụng "\ w +" có thể tạo ra một "" không mong muốn
"someText 👿🏅👿⚽️ pig".regex("\\w+")
Sẽ trả về mảng Chuỗi này
["someText", "️", "pig"]
Tôi thấy rằng giải pháp của câu trả lời được chấp nhận rất tiếc không biên dịch trên Swift 3 cho Linux. Đây là một phiên bản đã sửa đổi, sau đó, có:
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 []
}
}
Sự khác biệt chính là:
Swift trên Linux dường như yêu cầu bỏ NS
tiền tố trên các đối tượng Foundation mà không có Swift-native tương đương. (Xem đề xuất phát triển Swift # 86. )
Swift trên Linux cũng yêu cầu chỉ định các options
đối số cho cả quá trình RegularExpression
khởi tạo và matches
phương thức.
Vì một số lý do, việc ép buộc a String
thành một NSString
không hoạt động trong Swift trên Linux nhưng khởi tạo một mới NSString
với a String
như nguồn hoạt động.
Phiên bản này cũng hoạt động với Swift 3 trên macOS / Xcode với ngoại lệ duy nhất là bạn phải sử dụng tên NSRegularExpression
thay thế RegularExpression
.
Swift 4 không có NSString.
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)!])
}
}
}
extension String {
func match(_ regex: String) -> [[String]] {
let nsString = self as NSString
return (try? NSRegularExpression(pattern: regex, options: []))?.matches(in: self, options: [], range: NSMakeRange(0, count)).map { match in
(0..<match.numberOfRanges).map { match.range(at: $0).location == NSNotFound ? "" : nsString.substring(with: match.range(at: $0)) }
} ?? []
}
}
Trả về một mảng chuỗi 2 chiều:
"prefix12suffix fix1su".match("fix([0-9]+)su")
trả lại ...
[["fix12su", "12"], ["fix1su", "1"]]
// First element of sub-array is the match
// All subsequent elements are the capture groups
@ p4bloch nếu bạn muốn thu thập kết quả từ một loạt các dấu ngoặc đơn, thì bạn cần sử dụng rangeAtIndex(index)
phương pháp NSTextCheckingResult
thay vì range
. Đây là phương thức của @MartinR dành cho Swift2 từ bên trên, được điều chỉnh cho các dấu ngoặc đơn. Trong mảng được trả về, kết quả đầu tiên [0]
là toàn bộ quá trình chụp và sau đó các nhóm bắt riêng lẻ bắt đầu từ đó [1]
. Tôi đã nhận xét về map
hoạt động (vì vậy sẽ dễ dàng hơn để xem những gì tôi đã thay đổi) và thay thế nó bằng các vòng lặp lồng nhau.
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 []
}
}
Một trường hợp sử dụng ví dụ có thể là, giả sử bạn muốn chia một chuỗi title year
ví dụ: "Tìm Dory 2016", bạn có thể thực hiện điều này:
print ( matches(for: "^(.+)\\s(\\d{4})" , in: "Finding Dory 2016"))
// ["Finding Dory 2016", "Finding Dory", "2016"]
Hầu hết các giải pháp ở trên chỉ cho kết quả khớp đầy đủ do đó bỏ qua các nhóm thu thập, ví dụ: ^ \ d + \ s + (\ d +)
Để có được các trận đấu nhóm nắm bắt như mong đợi, bạn cần một cái gì đó như (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
}
}
Đây là cách tôi đã làm, tôi hy vọng nó mang lại một cái nhìn mới về cách thức hoạt động trên Swift.
Trong ví dụ dưới đây, tôi sẽ lấy bất kỳ chuỗi nào giữa []
var sample = "this is an [hello] amazing [world]"
var regex = NSRegularExpression(pattern: "\\[.+?\\]"
, options: NSRegularExpressionOptions.CaseInsensitive
, error: nil)
var matches = regex?.matchesInString(sample, options: nil
, range: NSMakeRange(0, countElements(sample))) as Array<NSTextCheckingResult>
for match in matches {
let r = (sample as NSString).substringWithRange(match.range)//cast to NSString is required to match range format.
println("found= \(r)")
}
Đây là một giải pháp rất đơn giản trả về một mảng chuỗi với các kết quả phù hợp
Swift 3.
internal func stringsMatching(regularExpressionPattern: String, options: NSRegularExpression.Options = []) -> [String] {
guard let regex = try? NSRegularExpression(pattern: regularExpressionPattern, options: options) else {
return []
}
let nsString = self as NSString
let results = regex.matches(in: self, options: [], range: NSMakeRange(0, nsString.length))
return results.map {
nsString.substring(with: $0.range)
}
}
Rất cảm ơn Lars Blumberg câu trả lời của anh ấy vì đã nắm bắt được các nhóm và các trận đấu đầy đủ với Swift 4 , điều này đã giúp tôi rất nhiều. Tôi cũng đã thực hiện một bổ sung cho nó cho những người muốn phản hồi lỗi .localizedDescription khi regex của họ không hợp lệ:
extension String {
func matchingStrings(regex: String) -> [[String]] {
do {
let regex = try NSRegularExpression(pattern: regex)
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))
: ""
}
}
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
}
Đối với tôi, lỗi localizedDescription giúp hiểu được điều gì đã xảy ra khi thoát, vì nó hiển thị những gì mà regex swift cuối cùng cố gắng triển khai.
Cate Blanchett đã bất chấp những lời khuyên hẹn hò điển hình khi cô gặp chồng mình.
Michael Sheen là một diễn viên phi lợi nhuận nhưng chính xác thì điều đó có nghĩa là gì?
Ngôi sao của Hallmark Colin Egglesfield chia sẻ về những cuộc gặp gỡ với người hâm mộ ly kỳ tại RomaDrama Live! cộng với chương trình INSPIRE của anh ấy tại đại hội.
Bạn sẽ phải phủi sạch đầu đĩa Blu-ray hoặc DVD để xem tại sao Northern Exposure trở thành một trong những chương trình nổi tiếng nhất của thập niên 90.
The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!
Bạn biết đấy, hai sản phẩm này là nguồn điện để làm sạch, riêng chúng. Nhưng cùng với nhau, chúng có một loạt công dụng hoàn toàn khác.
Thủy điện rất cần thiết cho lưới điện của Hoa Kỳ, nhưng nó chỉ tạo ra năng lượng khi có nước di chuyển. Bao nhiêu nhà máy thủy điện có thể gặp nguy hiểm khi các hồ và sông cạn kiệt?
Tóc tỉa từ các tiệm và các khoản quyên góp cá nhân có thể được tái sử dụng như những tấm thảm thấm dầu và giúp bảo vệ môi trường.
Trump và Giám đốc điều hành GM Mary Barra vào năm 2017. Kể từ khi tin tức nổ ra ngày hôm qua rằng General Motors đang cắt giảm việc làm ở Bắc Mỹ để chuẩn bị cho tương lai điện / xe tự hành / khủng khiếp có thể xảy ra của chúng tôi, gần như tất cả mọi người đã tự hỏi: Tổng thống Trump, người đã vận động tranh cử trên một nền tảng sẽ như thế nào của việc mang lại và duy trì việc làm ô tô của Mỹ, phải nói gì về điều này? Bây giờ chúng ta biết.
Bethesda cho biết ngày hôm nay trên Reddit, Fallout 76 sẽ có một bản vá lớn khác vào ngày 4 tháng 12, sẽ bắt đầu cố gắng khắc phục một số vấn đề lớn hơn của trò chơi. Ngoài ra, công ty cho biết họ có kế hoạch giao tiếp nhiều hơn trong tương lai về những gì nhóm Fallout 76 đang làm việc và khi nào các bản cập nhật mới cho trò chơi sẽ ra mắt.
Bạn có biết rằng một trong sáu bệnh nhân ở bệnh viện ở Hoa Kỳ được điều trị tại một cơ sở Công giáo? Và bạn có biết rằng quyền sở hữu của Công giáo hạn chế sâu sắc việc thiết lập các thủ tục y tế có thể được thực hiện trong các bệnh viện này, rõ ràng nhất là khi nói đến chăm sóc sức khỏe sinh sản? Bây giờ NPR báo cáo rằng sự gia tăng của các bệnh viện thuộc Công giáo đang gây khó khăn hơn cho các bác sĩ — những người bị cấm bởi các quy định do Hoa Kỳ viết.
Đường chân trời San Francisco bị che khuất bởi khói và khói mù do cháy rừng phía sau Đảo Alcatraz, Thứ Tư, ngày 14 tháng 11 năm 2018, ở San Francisco.
Nicky Hilton Rothschild's luggage got lost, but luckily she has an incredible closet to shop: Sister Paris Hilton's!
Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa. Từ Hollywood đến New York và mọi nơi ở giữa, hãy xem các ngôi sao yêu thích của bạn đang làm gì!
Các nhà điều tra đang xem xét liệu nhóm và nghi phạm có biết nhau trước vụ tấn công hay không
Vụ kiện, nêu tên một số học khu, lập luận rằng dự luật "Không nói đồng tính" được ban hành gần đây của Florida "có hiệu quả im lặng và xóa bỏ học sinh và gia đình LGBTQ +"
Cuối hè đầu thu là mùa hoài niệm. Những chiếc đèn đường chiếu ánh sáng của chúng qua những con đường đẫm mưa, và những chiếc lá dưới chân - màu đỏ cam tắt trong bóng chạng vạng - là lời nhắc nhở về những ngày đã qua.
Vào năm 2021, tôi khuyến khích bạn suy nghĩ lại mọi thứ bạn biết về khách hàng mà bạn phục vụ và những câu chuyện bạn kể cho họ. Lùi lại.
Vào ngày sinh nhật thứ 9 của Felix The Cat, tôi nhớ về một trong những mất mát lớn nhất trong cuộc đời trưởng thành của tôi - Sophie của tôi vào năm 2013. Tôi đã viết bài luận này và chia sẻ nó trên nền tảng này một thời gian ngắn vào năm 2013.
Tôi ghét từ "tàu đắm". Mọi người cảm thấy thoải mái trong la bàn đạo đức của riêng mình, và khi làm như vậy, họ thấy mình vượt qua sự phán xét.