美文网首页Swift
Swift 正则Regex

Swift 正则Regex

作者: zzzworm | 来源:发表于2023-11-14 16:34 被阅读0次

    Swift 正则Regex

    在Swift 5.7版本中,Apple在Swift对正则表达式的支持做了一个重大的更新,不但提供了一个Regex的新类型代表正则表达式,同时还提供了一个DSL语法来创建正则表达式。

    • SE-0350 introduces a new Regex type
    • SE-0351 introduces a result builder-powered DSL for creating regular expressions.
    • SE-0354 adds the ability co create a regular expression using /.../ rather than going through Regex and a string.
    • SE-0357 adds many new string processing algorithms based on regular expressions.

    这对编写正则表达式程序提供了极大的方便。在Swift之前的版本创建正则表达式仍然是使用NSRegularExpression,创建方式如下:

    // Match and capture one or more digits
    let pattern = #"(\d+)"#
    let regex = try Regex(pattern)
    

    使用NSRegularExpression的不足:

    1. 正则表达式字符串需要过滤正则表达式规则符号,比如/

    2. 正则表达式是否合规只能在运行时检测创建时是否有异常抛出

    现在,可以直接使用正则表达式创建一个Regex,并且在编译期就可以校验正则表达式是否合规。

    let regex = /(\w+)\s+(\d+)/
    

    针对输入字符串可以提取单词和数字组合。

    let input = "Tom 123 xyz"
    if let result = input.firstMatch(of: regex) {
      print(result.0)  // Tom 123  
      print(result.1)  // Tom  
      print(result.2)  // 123
      }
    

    正则表达式提取的结果存储在一个子串数组,0下标的第一个元素是匹配到的所有内容,第一个匹配内容存储在1下标元素中,以此类推。

    在提取多个元素时,可以通过读取匹配结果的output属性来获取匹配的tuple元组:

    if let match = input.firstMatch(of: regex) {
      let (matched, name, count) = match.output
    }
    

    可以对待提取的元组属性进行命名:

    let regex = /(?<name>\w+)\s+(?<count>\d+)/
    if let match = input.firstMatch(of: regex) {  
        print(match.name)   // Tom  
        print(match.count)  // 123
    }
    

    在编写多行正则表达式时,我们也可以使用#...#来包裹正则表达式字符串,从而使得正则表达式有更好的可读性。

    let regex = #/(?<name> \w+) \s+
      (?<count> \d+)/#
    

    对正则表达式查找结果可以有不同的方式方式:

    input.firstMatch(of: regex)
    input.wholeMatch(of: regex)
    input.prefixMatch(of: regex)
    

    使用Regex还可以进行文本的替换,剪裁或者分割:

    let line = "Tom   1234"
    let line1 = line.replacing(/\s+/,with:",")  // Tom,1234let 
    line2 = line.trimmingPrefix(/\w+\s+/)   // 1234
    let fields = line.split(separator: /\s+/)   // ["Tom","1234"]
    

    Regex Builder

    Swift 5.7中对正则表达式的更新,除了引入Regex类方便正则表达式的使用,还设计了一个更强大的DSL构建工具:Regex Builder。传统的正则表达式大家很难掌握正则表达式的各种规则,尤其是对匹配元素的条件和组合。但在Regex Builder中,允许你用结构化,更接近自然语义的方式来构造正则表达式。以上面 /(\w+)\s+(\d+)/的正则表达式为例,在Regex Builder中可以通过下面的语句进行组装:

    import RegexBuilder
    let regex = Regex { 
      Capture {   
      OneOrMore(.word) 
      }
      OneOrMore(.whitespace)
      Capture {
          OneOrMore(.digit)  
      }
    }
    let input = "Tom 123 xyz"
    if let match = input.firstMatch(of: regex) {
      let name = match.1    // Tom  
      let count = match.2   // 123
    }
    

    regex的语法规则通过大括号{}来进行组装,实际上是使用Swift的语法糖将每个条件规则包装到了闭包block中,使用

    One, OneOrMore, ZeroOrMore, ChoiceOf, and Optionally. 等可读性更强,更语义化的组件使得组装正则表达变得更容易,同时代码也更容易维护和理解。举个例子:

    let colorRegex = Regex {
      Capture {
          ChoiceOf {
                  "red"        
                  "green"        
                  "blue"    
          } 
       }
       ":" 
      One(.whitespace)    
      Capture(OneOrMore(.digit))  
      ZeroOrMore(.whitespace)
      Optionally(OneOrMore(.hexDigit))
     }
    

    使用该正则表达式解析json:

    let colors = [  "red: 255 FF",  "green: 0",  "blue: 128 80"]
    for color in colors {
      if let match = color.wholeMatch(of: colorRegex) {
          print(match.1, match.2)  
          }
     }
    // red 255
    // green 0
    // blue 128
    

    除了对正则表达的组装,Regex builder还支持转换语法:

    let regex = Regex {
      Capture {
          OneOrMore(.digit) 
      } transform: {    
          Int($0)  // Int?  
      }}
    

    通过转换函数可以将结果提取转换为需要的类型,将Capture替换为TryCapture,还可以避免输出无值的可选结果:

    let regex = Regex {
      TryCapture {    
          OneOrMore(.digit)  
      } transform: {    
          Int($0)  // Int  
      }
    }
    

    正则表达式匹配模式

    在正式表达式在匹配的时候有个重要的模式:贪婪匹配懒惰匹配

    当正则表达式中包含能接受重复的限定符时,通常的行为是匹配尽可能多的字符。这被称为贪婪匹配。如果是匹配尽可能少的字符,则称为懒惰匹配。一般正则表达式默认使用贪婪模式进行匹配。

    以一个提取数字的正则表达式为例:

    let regex = Regex {
      OneOrMore(.any, .eager)
      Capture(OneOrMore(.digit))
    }
    let line = "hello world 99 ----> 42"
    if let match = line.wholeMatch(of: regex) {
      let count = match.1  // 2
    }
    

    在第一规则OneOrMore作用是,使用贪婪匹配会匹配所有字符串直到数字4,留下最后的数字2满足第二规则OneOrMore(.digit),并被捕获为结果。所以输出结果为2。

    如果改变第一规则为懒惰匹配:

    let regex = Regex {
      OneOrMore(.any, .reluctant)
      Capture(OneOrMore(.digit))
    }
    let line = "hello world 99 ----> 42"
    if let match = line.wholeMatch(of: regex) {
      let count = match.1  // 42
    }
    

    则第一个规则在满足第二规则的贪婪匹配42后,会截至到4的前面空格。

    要改变一个正则表达式的匹配模式可以在表达式后追加repetitionBehavior修改器:

    Regex {}
    .repetitionBehavior(.reluctant)
    

    附录

    推荐一个工具:https://swiftregex.com/

    可以将正则表达式转换为Regex builder代码,还可以直接在线测试效果

    相关文章

      网友评论

        本文标题:Swift 正则Regex

        本文链接:https://www.haomeiwen.com/subject/impjwdtx.html