中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Swift中的RegexBuilder怎么正確使用

發布時間:2023-04-07 17:35:40 來源:億速云 閱讀:131 作者:iii 欄目:開發技術

本篇內容主要講解“Swift中的RegexBuilder怎么正確使用”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Swift中的RegexBuilder怎么正確使用”吧!

    前言

    在我們日常的項目開發中,經常會碰到和正則表達式打交道的時候。比如用戶密碼,通常會要求同時包含小寫字母、大寫字母、數字,并且長度不少于 8 位,以此來提高密碼的安全性。

    在 Swift 中,我們可以用正則表達式的字面量方式來進行實現。

    Regex 字面量

    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。

    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)")
    }

    RegexRepetitionBehavior

    該結構體是用來定義匹配的重復行為的,它有三個值:

    • 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 的強大功能來進行解析。

    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怎么正確使用”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    玉环县| 陕西省| 滦南县| 青神县| 平定县| 南汇区| 河西区| 英吉沙县| 通道| 沾益县| 桂阳县| 诸城市| 习水县| 吉水县| 抚松县| 临高县| 南昌县| 海兴县| 汕头市| 土默特右旗| 大方县| 三河市| 海门市| 陆良县| 福海县| 库尔勒市| 凌源市| 连城县| 新田县| 瓮安县| 财经| 克什克腾旗| 平武县| 远安县| 淅川县| 绥棱县| 元江| 册亨县| 平远县| 宁都县| 塔城市|