Swift – 封装一个正则表达式工具类(附:正则替换、正则匹配样例)

之前我写过一篇文章介绍如何使用正则表达式来验证用户名、邮箱、URL 等格式是否正确(点击查看)。除了验证数据外,我们还可以使用正则表达式进行文字替换、或者提取工作。下面通过样例进行演示。

一、封装一个正则工具类(Regex.swift)

由于 NSRegularExpression 使用起来十分繁琐,为方便使用,我们首先对它进行封装。增加一些常用的正则处理方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import Foundation
/// 基于NSRegularExpression api 的正则处理工具类
public struct Regex {
    private let regularExpression: NSRegularExpression
    
    //使用正则表达式进行初始化
    public init(_ pattern: String, options: Options = []) throws {
        regularExpression = try NSRegularExpression(
            pattern: pattern,
            options: options.toNSRegularExpressionOptions
        )
    }
    
    //正则匹配验证(true表示匹配成功)
    public func matches(_ string: String) -> Bool {
        return firstMatch(in: string) != nil
    }
    
    //获取第一个匹配结果
    public func firstMatch(in string: String) -> Match? {
        let firstMatch = regularExpression
            .firstMatch(in: string, options: [],
                        range: NSRange(location: 0, length: string.utf16.count))
            .map { Match(result: $0, in: string) }
        return firstMatch
    }
    
    //获取所有的匹配结果
    public func matches(in string: String) -> [Match] {
        let matches = regularExpression
            .matches(in: string, options: [],
                     range: NSRange(location: 0, length: string.utf16.count))
            .map { Match(result: $0, in: string) }
        return matches
    }
    
    //正则替换
    public func replacingMatches(in input: String, with template: String,
                                 count: Int? = nil) -> String {
        var output = input
        let matches = self.matches(in: input)
        let rangedMatches = Array(matches[0..<min(matches.count, count ?? .max)])
        for match in rangedMatches.reversed() {
            let replacement = match.string(applyingTemplate: template)
            output.replaceSubrange(match.range, with: replacement)
        }
        
        return output
    }
}
//正则匹配可选项
extension Regex {
    /// Options 定义了正则表达式匹配时的行为
    public struct Options: OptionSet {
        
        //忽略字母
        public static let ignoreCase = Options(rawValue: 1)
        
        //忽略元字符
        public static let ignoreMetacharacters = Options(rawValue: 1 << 1)
        
        //默认情况下,“^”匹配字符串的开始和结束的“$”匹配字符串,无视任何换行。
        //使用这个配置,“^”将匹配的每一行的开始,和“$”将匹配的每一行的结束。
        public static let anchorsMatchLines = Options(rawValue: 1 << 2)
        
        ///默认情况下,"."匹配除换行符(\n)之外的所有字符。使用这个配置,选项将允许“.”匹配换行符
        public static let dotMatchesLineSeparators = Options(rawValue: 1 << 3)
        
        //OptionSet的 raw value
        public let rawValue: Int
        
        //将Regex.Options 转换成对应的 NSRegularExpression.Options
        var toNSRegularExpressionOptions: NSRegularExpression.Options {
            var options = NSRegularExpression.Options()
            if contains(.ignoreCase) { options.insert(.caseInsensitive) }
            if contains(.ignoreMetacharacters) {
                options.insert(.ignoreMetacharacters) }
            if contains(.anchorsMatchLines) { options.insert(.anchorsMatchLines) }
            if contains(.dotMatchesLineSeparators) {
                options.insert(.dotMatchesLineSeparators) }
            return options
        }
        
        //OptionSet 初始化
        public init(rawValue: Int) {
            self.rawValue = rawValue
        }
    }
}
//正则匹配结果
extension Regex {
    // Match 封装有单个匹配结果
    public class Match: CustomStringConvertible {
        //匹配的字符串
        public lazy var string: String = {
            return String(describing: self.baseString[self.range])
        }()
        
        //匹配的字符范围
        public lazy var range: Range<String.Index> = {
            return Range(self.result.range, in: self.baseString)!
        }()
        
        //正则表达式中每个捕获组匹配的字符串
        public lazy var captures: [String?] = {
            let captureRanges = stride(from: 0, to: result.numberOfRanges, by: 1)
                .map(result.range)
                .dropFirst()
                .map { [unowned self] in
                    Range($0, in: self.baseString)
            }
            
            return captureRanges.map { [unowned self] captureRange in
                if let captureRange = captureRange {
                    return String(describing: self.baseString[captureRange])
                }
                
                return nil
            }
        }()
        
        private let result: NSTextCheckingResult
        
        private let baseString: String
        
        //初始化
        internal init(result: NSTextCheckingResult, in string: String) {
            precondition(
                result.regularExpression != nil,
                "NSTextCheckingResult必需使用正则表达式"
            )
            
            self.result = result
            self.baseString = string
        }
        
        //返回一个新字符串,根据“模板”替换匹配的字符串。
        public func string(applyingTemplate template: String) -> String {
            let replacement = result.regularExpression!.replacementString(
                for: result,
                in: baseString,
                offset: 0,
                template: template
            )
            
            return replacement
        }
        
        //藐视信息
        public var description: String {
            return "Match<\"\(string)\">"
        }
    }
}

 

二、使用样例

1,验证字符串格式

下面样例验证一个邮箱地址的格式是否正确。
1
2
3
4
5
6
7
8
9
10
11
//初始化正则工具类
let pattern = "^([a-z0-9_\\.-]+)@([\\da-z\\.-]+)\\.([a-z\\.]{2,6})$"
let regex = try! Regex(pattern)
//验证邮箱地址
let mailAddress = "admin@hangge.com"
if regex.matches(mailAddress) {
    print("邮箱地址格式正确")
}else{
    print("邮箱地址格式有误")
}

 

2,提取字符串

(1)获取第一个匹配结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//初始化正则工具类
let pattern = "([\\u4e00-\\u9fa5]+):([\\d]+)"
let regex = try! Regex(pattern)
//原始字符串
let str = "王大锤:123456,李子明:23457,李洛克:110"
//获取第一个匹配对象
if let first = regex.firstMatch(in: str) {
    print("--- 第一个匹配结果  ---")
    print(first)
    print("匹配字符串:", first.string)
    print("捕获组:", first.captures[0]!, first.captures[1]!)
    print("匹配范围:", first.range)
}

(2)获取所有的匹配结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//初始化正则工具类
let pattern = "([\\u4e00-\\u9fa5]+):([\\d]+)"
let regex = try! Regex(pattern)
//原始字符串
let str = "王大锤:123456,李子明:23457,李洛克:110"
//获取第一个匹配对象
for match in regex.matches(in: str) {
    print("\n--- 匹配结果  ---")
    print(match)
    print("匹配字符串:", match.string)
    print("捕获组:", match.captures[0]!, match.captures[1]!)
    print("匹配范围:", match.range)
}

3,字符串替换

(1)简单的替换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//初始化正则工具类
let pattern = "([\\u4e00-\\u9fa5]+):([\\d]+)"
let regex = try! Regex(pattern)
//原始字符串
let str = "王大锤:123456,李子明:23457,李洛克:110"
//只替换第1个匹配项
let out1 = regex.replacingMatches(in: str, with: "***", count: 1)
//替换所有匹配项
let out2 = regex.replacingMatches(in: str, with: "***")
  
//输出结果
print("原始的字符串:", str)
print("替换第1个匹配项:", out1)
print("替换所有匹配项:", out2)

(2)捕获组替换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//初始化正则工具类
let pattern = "([\\u4e00-\\u9fa5]+):([\\d]+)"
let regex = try! Regex(pattern)
//原始字符串
let str = "王大锤:123456,李子明:23457,李洛克:110"
//只替换第1个匹配项
let out1 = regex.replacingMatches(in: str, with: "$1的电话是$2", count: 1)
//替换所有匹配项
let out2 = regex.replacingMatches(in: str, with: "$1的电话是$2")
  
//输出结果
print("原始的字符串:", str)
print("替换第1个匹配项:", out1)
print("替换所有匹配项:", out2)

 

Swift脚本编程

用命令启动REPL时,使用的就是Bash Shell.

#!/usr/bin/swift

import Foundation

class Execution {

    class func execute(path: String, arguments: [String]? = nil) -> Int {

        let task = Process()

        task.launchPath = path

        if arguments != nil {

            task.arguments = arguments!

        }

        task.launch()

        task.waitUntilExit()

        return Int(task.terminationStatus)

    }

}

var status : Int = 0

status = Execution.execute(path: “/bin/ls”)

print(“Status = \(status)”)

status = Execution.execute(path: “/bin/ls”, arguments: [“/”])

print(“Status = \(status)”)

将此文件保存为:swiftScript3.sh

chmod +x swiftScript3.sh

./swiftScript3.sh