正则表达式

代码

Swift至今为止并没有在语言层面上支持正则表达式。我们可以自定义类似 =~ 的操作符来进行正则匹配。在Cocoa中我们可以使用 NSRegularExpression 来做正则匹配,因为做的是字符串正则匹配,所以 =~ 左右两边都是字符串。我们可以先写一个接受正则表达式的字符串,以此生成 NSRegularExpression 对象。一个简单的实现可能是下面这样的:

//在Cocoa中使用NSRegularExpression来做正则匹配。
struct RegexHelper {
    let regex : NSRegularExpression

    init (_ pattern: String) throws {
        try regex = NSRegularExpression(pattern: pattern,
                                        options: .caseInsensitive)
    }

    func match(_ input: String) -> Bool {
        let matches = regex.matches(in:input,
                                            options:[],
                                            range: NSMakeRange(0, input.characters.count))
        return matches.count > 0
    }

}

在使用的时候,比如我们要匹配一个邮箱地址,我们可以这样来使用:

        let mailPattern = "^([a-z0-9_\\.-]+)@([\\da-z\\.-]+)\\.([a-z\\.]{2,6})$"

        let matcher : RegexHelper
        do {
            matcher = try RegexHelper(mailPattern)
            let maybeMailAddress = "[email protected]"
            if matcher.match(maybeMailAddress) {
                print("有效的邮箱地址")
            }
        } catch {

        }
     // 输出:有效的邮箱地址

现在我们有了方便的封装,接下来实现一下 =~ 吧:

infix operator =~: ComparisonPrecedence

func =~(lhs: String, rhs: String) -> Bool {
    do {
        return try RegexHelper(rhs).match(lhs)
    }catch _ {
        return false
    }
}

这样我们就能使用类似于其他语言的正则匹配的方法了:

        let mailPattern = "^([a-z0-9_\\.-]+)@([\\da-z\\.-]+)\\.([a-z\\.]{2,6})$"

        let maybeMailAddress = "[email protected]"
        if maybeMailAddress =~ mailPattern {
            print("有效的邮箱地址")
        }
        // 输出:有效的邮箱地址

模式匹配

代码

从概念上讲正则匹配只是模式匹配的一个子集,但是在 Swift 里现在的模式匹配还很初级,也很简单,只支持最简单的相等匹配和范围匹配Swift 中,使用 ~= 来表示模式匹配的操作符。如果我们看看 API 的话,可以看到这个操作符有下面几种版本:

func ~=<T: Equatable>(a: T, b: T) -> Bool
func ~=<T>(lhs: _OptionalNilComparisonType, rhs: T?) -> Bool
func ~=<I: intervalType>(pattern: I, value: I.Bound) -> Bool

从上至下在操作符左右两边分别接收判等的类型,可以与 nil 比较的类型,以及一个范围输入和某个特定值,返回值很明了,都是是否匹配成功的 Bool 值。你是否有想起些什么呢...没错, 就是 Swift 中非常强大的 switch,我们来看看 switch 的几种常见用法吧:

  1. 可以判等的类型的判断
let password = "akfuv(3"
switch password {
    case "akfuv(3": print("密码通过")
    default       : print("密码通过")
}
  1. 对 Optional 的判断
let num: Int? = nil
switch num {
    case nil: print("没值")
    default : print("\(num)")
}
  1. 对范围的判断
let x = 0.5
switch x {
    case -1.0...1.0: print("区间内")
    default        : print("区间外")
}

Swift 的 switch 就是使用了 ~= 操作符进行模式匹配,case 指定的模式作为左参数输入,而等待匹配的被 switch 的元素作为操作符的右侧参数。只不过这个调用是由Swift隐式地完成的。于是我们可以发挥想象的地方就很多了,比如在 switch 中做case判断的时候,我们完全可以使用我们自定义的模式匹配方法来进行判断,有时候这会让代码变得非常简洁,具有条理。我们只需要按照需求重载 ~= 操作符就行了,接下来我们通过一个使用正则表达式做匹配的例子加以说明。

1.首先我们要做的是重载 ~= 操作符,让它接受一个 NSRegularExpression 作为模式,去匹配输入的 String

//自定义模式匹配
//1 重载 ~= 操作符  让它接收一个 NSRegularExpression 作为模式,去匹配输入的string
func ~=(pattern: NSRegularExpression,input: String) -> Bool {
    return pattern.numberOfMatches(in: input,
                                           options: [],
                                           range: NSMakeRange(0, input.characters.count)) > 0
}

2.然后为了简便起见,我们再添加一个将字符串转换为 NSRegularExpression 的操作符 (当然也可以使用 StringLiteralConvertible ,但是它不是这个 tip 的主题,在此就先不使用它了):

//2 添加一个将字符串转化为 NSRegularExpression 的操作符(也可以使用StringLiteralConvertible)

prefix operator ~/

prefix func ~/(pattern: String) -> NSRegularExpression {
    return try! NSRegularExpression(pattern: pattern, options: [])
}

3.现在,我们在 case 语句里使用正则表达式的话,就可以去匹配被 switch 的字符串了:

       let contact = ("http://onvcat.com", "[email protected]")
        let mailRegex: NSRegularExpression
        let siteRegex: NSRegularExpression

        //将字符串转化为  NSRegularExpression (正则表达式)
        mailRegex =
             ~/"^([a-z0-9_\\.-]+)@([\\da-z\\.-]+)\\.([a-z\\.]{2,6})$"
        siteRegex =
             ~/"^(https?:\\/\\/)?([\\da-z\\.-]+)\\.([a-z\\.]{2,6})([\\/\\w \\.-]*)*\\/?$"


        //swfit里的switch 就是使用~=操作符进行模式匹配,case指定的模式作为左参数输入,而等待匹配的被switch的元素作为操作符的右参数输入。
        //这个调用是由swift隐式地完成的。这里重载~=操作符,就可以按照我们自定义的模式进行匹配了
        switch contact {
        case (siteRegex, mailRegex):
            print("同时拥有有效的网站和邮箱")
        case (_, mailRegex):
            print("只拥有有效的邮箱")
        case (siteRegex, _):
            print("只拥有有效的网站")
        default:
            print("啥都没有")
        }
// 输出:同时拥有有效的网站和邮箱

where 和 模式匹配

代码

where 关键字在 Swift 中非常强大,但是往往容易被忽视。在这一节中,我们就来整理看看 where有哪些使用场合吧。

1.在 switch 语句中,我们可以使用 where来限定某些条件 case

        let name = ["王小二", "张三", "李四", "王二小"]
        name.forEach {
            switch $0 {
            case let x where x.hasPrefix("王"):
                print("\(x)是笔者本家")
            default:
                print("你好, \($0)")
            }

        }
        //输出:
        //王小二是笔者本家
        //你好, 张三
        //你好, 李四
        //王二小是笔者本家

这可以说是模式匹配的标准用法,对 case 条件进行限定可以让我们更灵活地使用 switch 语句。

2.在 for 中我们也可以使用 where 来做类似的条件限定:

        let num: [Int?] = [48, 99, nil];
        let n = num.flatMap {$0} //
        print(n)
        for score in n where score > 60 {
            print("及格啦 - \(score)")
        }
        //输出: 及格啦 - 99

for 循环中类似,我们也可以对可选绑定进行条件限定。不过 在 Swift 3 中,if letguard let 的条件不再使用 where 语句,而是直接和普通的条件判断一样,用逗号写在 if 或者 guard 的后面 :

        num.forEach {
            if let score = $0, score > 60 {
                print("及格啦 - \(score)")
            }
            else {
                print(":(")
            }
        }

        // 输出:
        // :(
        // 及格啦 - 99
        // :(

3.在泛型中对方法的类型进行限定。比如在标准库里对 RawRepresentable 协议定义 != 运算符定义时:

public func !=<T: RawRepresentable where T.RawValue: Equatable>(lhs: T, rhs: T) -> Bool

这里限定了 T.RawValue 必须要遵守 Equatable 协议,这样我们才能通过对比 lhsrhsrawValue 是否相等,进而判断这两个 RawRepresentable 值是否相等。如果没有 where 的保证的话,下面的代码就无法编译。同时,我们也限定了那些 rawValue 无法判等的 RawRepresentable 类型不能进行判等。

public func !=<T: RawRepresentable where T.RawValue: Equatable>(lhs: T, rhs: T) -> Bool {
      return lhs.rawValue != rhs.rawValue
}

4.在 Swift 2.0 中,引入了 protocol extension。在有些时候,我们会希望一个协议扩展的默认实现只在某些特定的条件下适用,这时我们就可以用 where 关键字来进行限定。标准库中的协议扩展大量使用了这个技术来进行限定,比如 Sequencesorted 方法就被定义在这样一个类型限制的协议扩展中:

extension Sequence where Self.Iterator.Element : Comparable {
        public func sorted() ->[Self.Iterator.Element]
}

很自然,如果 Sequence (比如一个 Array ) 中的元素是不可比较的,那么 sorted 方法自然也就不能适用了:

        let sortableArray: [Int] = [3,1,2,4,5]
        let unSortableArray: [Any?] = ["Hello", 4, nil]

        print(sortableArray.sorted()) //[1, 2, 3, 4, 5]
        print(unSortableArray.sorted()) //编译错误

results matching ""

    No results matching ""