怎样在Swift中使用正则表达式

Posted Cocoa开发者社区

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了怎样在Swift中使用正则表达式相关的知识,希望对你有一定的参考价值。


正则表达式(Regular expression, regex)允许我们在几秒钟内在成千上万文档间进行复杂检索与替换,自从诞生50多年来它依旧广泛使用。


在这篇文章中,我会讲解在Swift中正则表达式的基本用法。我们会从易到难,详细讲解一些最重要的正则表达式语法,以及一些有用的扩展。


NSRegularExpression:如何在字符串中匹配正则表达式


NSRegularExpression类让我们可以用正则表达式查找替换子字符串,它可以简洁灵活地描述文本。例如,如果你想从"My name is Taylor Swift"中提取出"Taylor Swift",可以写一个匹配文本“My name is”的正则表达式,它的后面可以是任何文本,之后把它传递给NSRegularExpression类。


具体可见下面代码。注意我们要提取出的是第二范围,因为第一范围是匹配的字符串,而第二范围才是"Taylor Swift"部分。

do {
    let input = "My name is Taylor Swift"
    let regex = try NSRegularExpression(pattern: "My name is (.*)", options: NSRegularExpression.Options.caseInsensitive)
    let matches = regex.matches(ininput, options: [], range: NSRange(location: 0length: input.utf16.count))

    if let match = matches.first {
        let range = match.range(at:1)
        if let swiftRange = Range(rangeininput) {
            let name = input[swiftRange]
        }
    }
} catch {
    // regex was bad!
}


正则表达式的详细讲解


让我们从几个简单例子开始,方便不熟悉的人了解正则表达式。正则表达式,简称regex,用于让我们在字符串中进行模糊检索。例如我们知道”cat”包含”at”,但如果我们检索所有以“at”结尾的3字母单词该怎么做呢?


正则表达式就用于解决这个问题,尽管由于Objective-C的基础,它们的语法有些不太灵巧。


1. 首先,定义你想检索的字符串:

let testString = "hat"


之后创建NSRange实例来表示整个字符串的长度

let range = NSRange(location: 0, length: testString.utf16.count)


这里使用utf16来避免类似表情符号等带来的问题


2. 之后使用正则表达式语法创建NSRegularExpression实例

let regex = try! NSRegularExpression(pattern: "[a-z]at")


[a-z]在正则表达式中用于指定a到z之间任意字母。实际使用中你可能会提供一个无效的正则表达式,但是这里我们有了一个硬编码的正确正则表达式,所以就不需要查找错误了。


3. 最后在创建好的正则表达式调用firstMatch(in:),输入要检索的字符串,一些特殊选项,和字符串的范围。如果字符串匹配正则表达式,就会返回数据,否则就是nil。所以如果你想检查字符串是否完全匹配,就用firstMatch(in:)的结果和nil比较:

regex.firstMatch(in: testString, options: [], rangerange) != nil


这里必须要用到NSRange——尽管这个API是为NSString设计,和Swift衔接的不太好。Swift String Manifesto可能会替换它,但看起来还要很久。


正则表达式“[a-z]at”会成功匹配“hat”,和“cat”, “sat”, “mat”, “bat”等等——我们只要关注想匹配什么,NSRegularExpression会处理好它。


让NSRegularExpression用起来更简单


接下里会展示更多的正则表达式语法,首先来看看如何让NSRegularExpression稍微好用一些


现在我们的要3行Swift代码来匹配一个简单字符串

let range = NSRange(location: 0, length: testString.utf16.count)
let regex = try! NSRegularExpression(pattern: "[a-z]at")
regex.firstMatch(in: testString, options: [], range: range) != nil


我们可以从多种方式改进,不过最有效的是扩展NSRegularExpression,让创建和匹配表达式更简单。


首先第一行:

let regex = try! NSRegularExpression(pattern: "[a-z]at")


我提到过,创建一个NSRegularExpression实例可能导致错误,因为可能会提供一个非法的正则表达式。比如[a-zat,忘记了]


结果就是,通常会用try!创建NSRegularExpression实例。然而这会导致lint工具(如SwiftLint)的破坏。所以好一点的方法是创建一个方便的初始化,能正确创建正则表达式,或者在开发时能生成一个断言失败。

extension NSRegularExpression {
    convenience init(_ pattern: String) {
        do {
            try self.init(pattern: pattern)
        } catch {
            preconditionFailure("Illegal regular expression: \(pattern).")
        }
    }
}


注意:如果你的app需要用户写正则表达式,你需要使用NSRegularExpression(pattern:)初始化,这样可以更好的处理错误。


之后这些行:

let range = NSRange(location: 0, length: testString.utf16.count)
regex.firstMatch(in: testString, options: [], rangerange) != nil


第一行创建了一个包含整个字符串的NSRange,第二行则是在文本中查找first match。但这是很笨的方法,因为大多时候你想查找输入的整个字符串,用firstMatch(in:)与nil判定会弄混你的意图。


所以,用另一个扩展来替代它,它把下面代码包含在一个简单的matches()方法中。

extension NSRegularExpression {
    func matches(_ string: String) -> Bool {
        let range = NSRange(location: 0, length: string.utf16.count)
        return firstMatch(in: string, options: [], rangerange) != nil
    }


如果你把这两个扩展合并,就可以更轻松的创建和检索正则表达式了。

let regex = NSRegularExpression("[a-z]at")
regex.matches("hat"


我们可以进一步通过运算符重载让Swift包含的,~=,运算符适用于正则表达式:

extension String {
    static func ~= (lhs: StringrhsString) -> Bool {
        guard let regex = try? NSRegularExpression(pattern: rhs) else { return false }
        let range = NSRange(location: 0length: lhs.utf16.count)
        return regex.firstMatch(in: lhs, options: [], range: range) != nil
    }
}


通过上面代码,我们可以在一句话的左边使用任意字符,右边用正则表达式。

"hat" ~= "[a-z]at"


注意:创建NSRegularExpression实例会有一定消耗,所以如果你想要反复使用一个正则表达式,最好把NSRegularExpression实例保存起来。


正则表达式语法学之旅


我们已经使用了[a-z]来表示“a”到“z”之间任意字母,在正则表达式中这是一个字符类。它让你指定要匹配的一组字母,可以通过制定的字母列表匹配,或者通过一段字符范围匹配。


正则表达式范围不一定是整个字母表,你可以用[a-t] 来排除“u”到“z”之间的字母。另外,如果你想特别指定一些字母,只需要像这样单独列出它们:

[csm]at


正则表达式默认区分大小姐写,也就是说“Cat”和“Mat”不会在“[a-z]at”被匹配。如果你想忽略大小写,可以使用“[a-zA-Z]at”,或者创建你自己的NSRegularExpression对象,并标记.caseInsensitive


除了大小写以外,你可以通过字符类指定数字范围。最常用的是[0-9]表示任何数字,或[A-Za-z0-9]表示任何字母数字混编字符,也可以用[A-Fa-f0-9]来表示16进制数字。


如果你想匹配一个字符序列,还需要一个叫做量词(quantifier)的概念。它用于表示字符出现的数量。


最常用的是星号量词,*,意思是匹配0个或更多。量词在它们修饰的字符后出现,就像下面这样:

let regex = NSRegularExpression("ca[a-z]*d")

 

这句话先查找“ca”,之后是0或多个从“a”到“z”的字母,最后是“d”——它能匹配“cad”, “card”, “clamped”等等。


除了*之外,还有2个类似的量词 + 和 ? 。 + 意味着“1个或更多”,与 * 的“0个或更多”有点区别。而 ? 的意思是”0或1个”


这些量词是正则表达式基础内容,希望大家能确实理解它们的区别,比如下面3个正则表达式


  1. ca[a-z]*d

  2. ca[a-z]+d

  3. ca[a-z]?d


并想想如果给出字符串“cd”或“clamped”,哪些能够匹配。


如果需要,可以用大括号 { 和 } 来更详细的指定匹配数量,比如[a-z]{3}意味着匹配3个小写字母。



此外还可以用大括号指定范围,它可以是有界限的或无界限的。比如[a-z]{1,3}代表匹配1,2,或3个小写字母。[a-z]{3,}代表匹配3个或更多个


最后,元字符(meta-characters)是特殊字符,正则表达式中有特别的意义,在这里介绍其中几个使用最频繁的。


首先其中是最常用,也是最滥用的 . 字符。它可以匹配除了换行符以外任意一个字符。比如正则表达式c.t可以匹配“cat”,但不能匹配“cart”。如果你把 . 和 * 量词共同使用,就意味着匹配1个或多个除了换行符以外所有字符,这可能是你最常见的正则表达式了。


.* 常用的原因也显而易见:不需要具体设计一个特别的正则表达式,.* 就可以匹配几乎一切了。然而问题是,特定化本来就是正则表达式的要点之一,你可以在文本中精确查找一些字符并操作它们。而太多人完全依赖 .* ,却没有意识到这可能会给他们的表达式带来难以察觉的错误。



但是这样就带来一个问题,它会匹配“123-4567”, “123-4567890”, 或 “123-456-789012345”。为了让[0-9]{3}与[0-9]{4}匹配上,.* 会匹配尽可能多的字符


所以这里要用字符类与量词,比如[0-9]{3}[ -]*[0-9]{4},代表3个数字,之后0个或更多空格与连接线,之后4个数字。或者使用不包含字符类,即用它来匹配数字以外的字符,如[0-9]{3}[^0-9]+[0-9]{4},会匹配空格,连接线,斜杠等等,而不会匹配数字。


链接:https://www.hackingwithswift.com/articles/108/how-to-use-regular-expressions-in-swift

翻译:cocoachina


相关推荐:





以上是关于怎样在Swift中使用正则表达式的主要内容,如果未能解决你的问题,请参考以下文章

无法让 Swift 中的正则表达式匹配“不包含”

Regex 正则表达式中几个符号([ ] ^ ?: ?= ?!)的概念

在 Swift 3 中使用 switch 语句构造正则表达式

通过 Java 正则表达式提取 semver 版本字符串的片段

swift 允许正则表达式在swift中扩展

Swift 代码在 Playground 中有效,但在命令行工具中无效