Swift:正規表現による文字列の抽出と置換

ここでは正規表現を使って、含有(include)、抽出(extractAll)、置換(replaceAll)を行う関数を紹介します。 これらの関数は、このページ末尾の“参考サイト”から集めてきたものです。尚、関数名や引数名は変えています。

また、関数 replaceAllByRule は、正規表現の中で(・・・)でグループ化された capturing group(subexpression)に置換ルールを適用することによって置換します。 下記の例では、文字列 "1+2,3+4,5+6" から正規表現 #"(\d+)\+(\d+)"# で2箇所のサブマッチ文字列を抽出し、Int型にキャストし、それらを加算した上で置換しています。結果は 3,7,11 となります。マッチした文字列全体は $0(0) で、i番目のサブマッチ文字列は $0(i) で取り出せます。

注)関数 replaceAllByRule は私の作品ですが、ライセンスフリーなので自由にご利用いただけます^^
注)下記のプログラムでは、文字列に #" ・・・ "# を使っているので、Swift 5 以降にしか対応していません。Swift 5 対応の paiza.IO では問題なく動作します。

regex.swift

import Foundation

extension String {
  func include(pattern:String) -> Bool {
    let regex = try! NSRegularExpression(pattern:pattern)
    return regex.firstMatch(in:self, range:self.range(of:self)) != nil
  }

  func extractAll(pattern:String) -> [String] {
    let regex = try! NSRegularExpression(pattern:pattern)
    return regex.matches(in:self, range:self.range(of:self)).map { String(self[Range($0.range, in:self)!]) }
  }

  func replaceAll(pattern:String, with:String) -> String {
    let regex = try! NSRegularExpression(pattern:pattern)
    return regex.stringByReplacingMatches(in:self, range:self.range(of:self), withTemplate:with)
  }

  func replaceAllByRule(pattern:String, replacementRule:((Int) -> String) -> String) -> String {
    let regex = try! NSRegularExpression(pattern:pattern)
    var string = self
    var location = 0
    while let firstMatch = regex.firstMatch(in:string, range:NSRange(location..<string.count)) {
      let range = firstMatch.range(at:0)  // 最初にマッチした文字列全体のrange
      let substring = { string.substring(with:firstMatch.range(at:$0)) }  // $0番目のサブマッチ文字列を取り出す関数
      let replacementString = replacementRule(substring)
      string = string.replacingCharacters(in:range, with:replacementString)
      location = range.location + replacementString.count
    }
    return string
  }
}

print("Swift".include(pattern:#"S\w*t"#))
print("St Swt8Swit".extractAll(pattern:#"S\w*t"#))
print("St Swt8Swit".replaceAll(pattern:#"S\w*t"#, with:"Swift"))
print("1+2,3+4,5+6".replaceAll(pattern:#"(\d+)\+(\d+)"#, with:"$2-$1"))
print("1+2,3+4,5+6".replaceAllByRule(pattern:#"(\d+)\+(\d+)"#) { String(Int($0(1))! + Int($0(2))!) })
 

 

〈実行結果〉

true
["St", "Swt8Swit"]
Swift Swift
2-1,4-3,6-5
3,7,11


補足

extractAll の中の $0.range の挙動は、次のようになります。


import Foundation

let pattern = #"S\w*t"#
let string = "St Swt Swit"
let regex = try! NSRegularExpression(pattern:pattern)
print(regex.matches(in:string, range:string.range(of:string)).map { $0.range })
 

 

〈実行結果〉

[{0, 2}, {3, 3}, {7, 4}]


参考サイト