您好,登錄后才能下訂單哦!
本篇內容主要講解“Swift中的RegexBuilder怎么正確使用”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Swift中的RegexBuilder怎么正確使用”吧!
在我們日常的項目開發中,經常會碰到和正則表達式打交道的時候。比如用戶密碼,通常會要求同時包含小寫字母、大寫字母、數字,并且長度不少于 8 位,以此來提高密碼的安全性。
在 Swift 中,我們可以用正則表達式的字面量方式來進行實現。
Regex 字面量實現代碼:
let regex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$/ let text = "Aa11111111" print(text.matches(of: regex).first?.output) // Optional("Aa11111111")
通過上述代碼可以看到,//
通過兩個斜線就可以來生成正則的字面量。用字面量的方式確實可以使代碼很簡潔,但簡潔的代價就是很難看懂,對后面的代碼維護也造成了很大的困難。
就像網上盛傳的一句梗一樣:“我有一個問題,所以我寫了一個正則表達式。現在,我有了兩個問題。”????
對于 Regex 難懂且難維護的問題,Swift 的開發團隊給出的方案就是:RegexBuilder。
假設我們有一個字符串"name: John Appleseed, user_id: 100",想要提取其中user_id的值。 首先第一步,先導入 RegexBuilder:
import RegexBuilder
接著,通過結構體 Regex
來構建正則語句:
let regex = Regex { "user_id:" // 1 OneOrMore(.whitespace) // 2 Capture(.localizedInteger(locale: Locale(identifier: "zh-CN"))) // 3 }
第一行代碼匹配的是固定字符串:"user_id",第二行代碼匹配的是一個或者多個空格,第三行代碼則是匹配的整型數字。
localizedInteger
會將匹配到的數字自動轉為整型,比如下面的例子:
let input = "user_id: 100.11" let regex = Regex { Capture(.localizedInteger(locale: Locale(identifier: "zh-CN"))) } if let match = input.firstMatch(of: regex) { print("Matched: \(match.0)") // Matched: 100.11 print("User ID: \(match.1)") // User ID: 100 }
雖然匹配的是 100.11,但輸出的仍然是 100。
最后,就可以通過 macth 的相關函數來進行數據提取了:
if let match = input.firstMatch(of: regex) { print("Matched: \(match.0)") print("User ID: \(match.1)") }
該結構體是用來定義匹配的重復行為的,它有三個值:
edger:會盡可能多的去匹配輸入的字符,必要的時候會回溯。默認為edger
reluctant:會盡可能少的去匹配輸入的字符,它會根據你的需求來一點點增大匹配區域,以完成匹配。
possessive:會盡可能多的去匹配輸入的字符,不會回溯。
比如下面這個例子:
let testSuiteTestInputs = [ "2022-06-06 09:41:00.001", "2022-06-06 09:41:00.001.", "2022-06-06 09:41:00.001."] let regex = Regex { Capture(OneOrMore(.any)) Optionally(".") } for line in testSuiteTestInputs { if let (dateTime) = line.wholeMatch(of: regex)?.output { print("Matched: \(dateTime)\"") } }
因為這三條數據最后的.
是不一定有的,所以我們的正則有一個 Optionally(".")
。但匹配出來的 dateTime 還是會帶 .
。因為 edger 會匹配所有的字符包含最后的點在內,這樣 Optionally(".")
根本不會起作用。
改成 Capture(OneOrMore(.any, .reluctant))
則會修復這個問題。因為 reluctant
它是匹配盡可能少的輸入,所以最后的Optionally(".")
會執行。
在 Swift 5.7 中,Foundation 框架也對 RegexBuilder 進行適配。所以對于 Date、URL等類型,我們可以借助 Foundation 的強大功能來進行解析。
假如,我們在做一個金融相關的 APP,為了兼容一些老數據,需要將一些字符串類型的數據轉為結構體。
這是我們的字符串數據:
let statement = """ CREDIT 2022/03/03 張三 ¥2,000,000.00 DEBIT 03/03/2022 Tom $2,000,000.00 DEBIT
這是我們需要轉的結構體:
struct Trade { let type: String let date: Date let name: String let count: Decimal }
下面這個就是我們需要編寫的 Regex:
let regex = Regex { Capture { /CREDIT|DEBIT/ } OneOrMore(.whitespace) Capture { One(.date(.numeric, locale: Locale(identifier: "zh_CN"), timeZone: .gmt)) } OneOrMore(.whitespace) Capture { OneOrMore(.word) } OneOrMore(.whitespace) Capture { One(.localizedCurrency(code: "CNY", locale: Locale(identifier: "zh_CN"))) } }
首先,我們需要匹配固定的字符串:CREDIT/DEBIT,接著是匹配一個或者多個空格。
接下來就是 Foundation 的重頭戲了,對于日期類型的字符串,我們并不需要寫一些匹配年月日規則的正則,只需要借助 Foundation 內嵌的功能即可。這樣做不僅省去了我們自己編寫的時間,更重要的是:官方寫的要比我們自己寫的更能保證代碼的正確性。
需要注意的是,Apple 推薦我們顯式的寫出 locale 屬性,而不是下面這種跟隨系統寫法 :
?
One(.date(.numeric, locale: Locale.current, timeZone: TimeZone.current))
因為這種寫法會帶來多種預期,并不能保證數據的確定性。
匹配完日期,接著就是對空格和用戶名的匹配。最后,是對交易金額的匹配,金額也是 Foundation 提供的函數來進行的匹配。
測試代碼:
let result = statement.matches(of: regex) var trades = [Trade]() result.forEach { match in let (_, type, date, name, count) = match.output trades.append(Trade(type: String(type), date: date, name: String(name), count: count)) } print(trades) // [SwiftDemo.Trade(type: "CREDIT", date: 2022-03-03 00:00:00 +0000, name: "張三", count: 2000000), SwiftDemo.Trade(type: "DEBIT", date: 2022-03-05 00:00:00 +0000, name: "李三", count: 33.27)]
通過打印可以得知,輸出的結果并不符合預期,漏掉了 Tom 那條數據。漏掉的原因可以通過代碼一眼得知:因為對日期和金額我們顯式的指定了是中國的格式,顯然03/03/2022
這種格式是不符合年月日的格式的。這也體現了顯式指定格式的好處:方便排查問題。
我們只要將日期格式轉為年月日格式,再將 $ 轉為 ¥ 即可讓正則正確匹配。
首先,我們需要根據 currency 來來返回正確的 Date 類型:
func pickStrategy(_ currency: Substring) -> Date.ParseStrategy { switch currency { case "$": return .date(.numeric, locale: Locale(identifier: "en_US"), timeZone: .gmt) case "¥": return .date(.numeric, locale: Locale(identifier: "zh_CN"), timeZone: .gmt) default: fatalError("We found another one!") } }
接著,編寫正則表達式來獲取相應的字符串字段:
let regex1 = #/ (?<date> \d{2} / \d{2} / \d{4}) (?<name> \P{currencySymbol}+) (?<currency> \p{currencySymbol}) /#
注:#//#
格式為 Swift 中運行時正則表達式的格式。
最后,再調用 replace 函數來進行符合正則的字符替換:
statement.replace(regex1) { match -> String in print(match.currency) let date = try! Date(String(match.date), strategy: pickStrategy(match.currency)) // ISO 8601, it's the only way to be sure let newDate = date.formatted(.iso8601.year().month().day()) return newDate + match.name + "¥" } statement = statement.replacingOccurrences(of: "-", with: "/")
這樣,我們就能解析出符合我們需求的 Trade 類型的數據了。
到此,相信大家對“Swift中的RegexBuilder怎么正確使用”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。