33
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Swift Regexでキャプチャや名前付きキャプチャを使う

Last updated at Posted at 2022-07-18

Swift 5.7で、Swift Regexが導入されます。これまでSwiftで正規表現を使う場合は NSRegularExpression を使っていましたが、言語組み込みの機能としてサポートされます。

他の言語の正規表現の機能と同様に、キャプチャの機能があり、名前付きキャプチャもあります。ここでは、Swift Regexのキャプチャと名前付きキャプチャの記法をまとめます。

Swift Regexの記法

Swift Regexでは、正規表現の記法が2通りあります。

1つはRegexリテラルで、次のような記法です。一般的な正規表現の記法と同様です。

let regex = /\d{4}-\d{2}-\d{2}/

もう1つはRegexビルダーで、次のような記法です。こちらはSwift独自の記法です。冗長にはなりますが、意味が分かりやすくなります。

let regex = Regex {
    Repeat(count: 4) { .digit }
    "-"
    Repeat(count: 2) { .digit }
    "-"
    Repeat(count: 2) { .digit }
}

RegexリテラルとRegexビルダーのどちらの記法で書いても、使い方は同様です。

let match = "2022-07-18".wholeMatch(of: regex)
if let match {
    print(match.0) // -> 2022-07-18
}

Regexリテラルでのキャプチャ

マッチした文字列の一部分を取り出して使うには、キャプチャを使います。キャプチャは ( ) を使って書きます。

let regex = /(\d{4})-(\d{2})-(\d{2})/

let match = "2022-07-18".wholeMatch(of: regex)
if let match {
    print("\(match.1)\(match.2)\(match.3)日") // -> 2022年07月18日
}

キャプチャした文字列は連番で参照します。しかし、キャプチャする部分が増えると、連番では分かりにくくなります。

そこで、キャプチャした部分に名前をつける、名前付きキャプチャという記法があります。これは (?<name>pattern) という書き方をします。

let regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/

let match = "2022-07-18".wholeMatch(of: regex)
if let match {
    print("\(match.year)\(match.month)\(match.day)日") // -> 2022年07月18日
}

連番の代わりに名前で参照できるようになり、分かりやすくなりました。

なお、名前付きキャプチャの記法はもうひとつあります。それは (?P<name>pattern) という書き方で、名前付きキャプチャを最初にサポートしたPythonはこの記法でした。上述の (?<name>pattern) という書き方は、.NETが名前付きキャプチャをサポートしたときの記法です。

Regexビルダーでのキャプチャ

Regexビルダーの場合、キャプチャは Capture を使って書きます。

let regex = Regex {
    Capture { Repeat(count: 4) { .digit } }
    "-"
    Capture { Repeat(count: 2) { .digit } }
    "-"
    Capture { Repeat(count: 2) { .digit } }
}

let match = "2022-07-18".wholeMatch(of: regex)
if let match {
    print("\(match.1)\(match.2)\(match.3)日") // -> 2022年07月18日
}

やはり、キャプチャした文字列は連番で参照します。

名前付きキャプチャは Capture(as:) を使って書きます。

let year = Reference(Substring.self)
let month = Reference(Substring.self)
let day = Reference(Substring.self)
let regex = Regex {
    Capture(as: year) { Repeat(count: 4) { .digit } }
    "-"
    Capture(as: month) { Repeat(count: 2) { .digit } }
    "-"
    Capture(as: day) { Repeat(count: 2) { .digit } }
}

let match = "2022-07-18".wholeMatch(of: regex)
if let match {
    print("\(match[year])\(match[month])\(match[day])日") // -> 2022年07月18日
}

Regexビルダーの場合は、Reference を用意しておく必要があります。また、参照のしかたが、Regexリテラルでは match.year だったところが match[year] に変わります。

TryCapture

Regexビルダーでのキャプチャの場合、TryCapture を使うとキャプチャした文字列を変換できます。

let regex = Regex {
    TryCapture { Repeat(count: 4) { .digit } } transform: { Int($0) }
    "-"
    TryCapture { Repeat(count: 2) { .digit } } transform: { Int($0) }
    "-"
    TryCapture { Repeat(count: 2) { .digit } } transform: { Int($0) }
}

let match = "2022-07-18".wholeMatch(of: regex)
if let match {
    print("\(match.1)\(match.2)\(match.3)日")
}

名前付きキャプチャでも TryCapture が使えます。この場合、Reference の型も変わります。

let year = Reference(Int.self)
let month = Reference(Int.self)
let day = Reference(Int.self)
let regex = Regex {
    TryCapture(as: year) { Repeat(count: 4) { .digit } } transform: { Int($0) }
    "-"
    TryCapture(as: month) { Repeat(count: 2) { .digit } } transform: { Int($0) }
    "-"
    TryCapture(as: day) { Repeat(count: 2) { .digit } } transform: { Int($0) }
}

let match = "2022-07-18".wholeMatch(of: regex)
if let match {
    print("\(match[year])\(match[month])\(match[day])日")
}

Regexリテラルでの後方参照

キャプチャした文字列をその正規表現の中で使うには、後方参照を使います。キャプチャを後方参照するには \数字 という書き方をします。

let regex = /([a-z]+) \1/

let match = "the the".wholeMatch(of: regex)
if let match {
    print(match.1) // -> the
}

名前付きキャプチャを後方参照するには \k<name> という書き方をします。

let regex = /(?<word>[a-z]+) \k<word>/

let match = "the the".wholeMatch(of: regex)
if let match {
    print(match.word) // -> the
}

なお、名前付きキャプチャの後方参照の記法はもうひとつあります。それは (?P=name) という書き方で、Pythonはこの記法でした。上述の \k<name> という書き方は.NETの記法です。

Regexビルダーでの後方参照

Regexビルダーの場合、後方参照は Capture を使って書きます。名前付きキャプチャを使う必要があります。

let word = Reference(Substring.self)
let regex = Regex {
    Capture(as: word) { OneOrMore(/[a-z]/) }
    One(.whitespace)
    Capture(word)
}

let match = "the the".wholeMatch(of: regex)
if let match {
    print(match[word]) // -> the
}

参考

以下を参考にさせていただきました。ありがとうございます。

また、筆者がSwift Regexについて勉強会で登壇したスライドも挙げておきます。

33
21
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
33
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?