Swift の正規表現はどうなっているのか以前気になって調べたところ、 Foundation には正規表現がないため、 NSRegularExpression を使うとのことで、実際に試しに使ってみたところ、使い勝手がかなり悪くて使うのを諦めました。そこで、 C 言語に regex.h という grep などで使われているらしい正規表現のヘッダーがあるようですので、こちらを Swift から呼び出してみるプログラムを書いてみました。

ディレクトリー構造はこんな感じ。
$ tree Sources/
Sources/
├── CRegexp
│ ├── c_regexp.c
│ └── include
│ └── c_regexp.h
└── Sample
├── CRegexp.swift
└── main.swift
CRegexp モジュールでは Swift から呼び出すのがややこしそうな部分のコードを C で書いている。 Swift から呼び出す関数は次の通り。
#ifndef C_REGEXP_H #define C_REGEXP_H #include <regex.h> #ifndef REG_OK #define REG_OK 0 #endif typedef struct { int value; } RegCode; typedef struct { regex_t *ptr; } RegPtr; typedef struct { regmatch_t *match_ptr; int size; } RegMatch; typedef struct { regmatch_t *ptr; } RegMatchElement; RegCode MakeRegexResult(int value); RegPtr NewRegex(); RegMatch NewRegMatch(int size); RegCode regex_compile(regex_t *regex_ptr, const char *pattern); RegCode regex_exec(const regex_t *ptr_regex_t, const char *target_string, regmatch_t *regmatch, int size); regoff_t regmatch_start_index(const regmatch_t *regmatch); regoff_t regmatch_end_index(const regmatch_t *regmatch); RegMatchElement regex_extract(const regmatch_t *regmatch, int index); void regex_free(RegPtr *ptr_regex); void regmatch_free(RegMatch *reg_match); #endif
CRegexp.swift がこの C のコードを呼び出すためのラッパーのような位置づけになる。
import Foundation import CRegexp protocol RegexMatcher { func matches() -> Bool } class Regex { private var rawPointer: RegPtr = NewRegex() let pattern: String init?(pattern: String) { self.pattern = pattern let regexPtr = rawPointer.ptr guard var ptn = pattern.cString(using: .utf8) else { return nil } let regCode: RegCode = regex_compile(regexPtr, &ptn) if regCode.hasError() { return nil } } deinit { regex_free(&rawPointer) } func matcher(for targetString: String, expectedItemCount: Int = 32) -> RegexMatcher { let regexPtr = rawPointer.ptr let size: Int32 = Int32(expectedItemCount) var regMatch: RegMatch = NewRegMatch(size) guard var target = targetString.cString(using: .utf8) else { regmatch_free(®Match) return NoMatch() } let regMatchPtr = regMatch.match_ptr let regCode: RegCode = regex_exec(regexPtr, &target, regMatchPtr, size) if regCode.hasError() { regmatch_free(®Match) return NoMatch() } return RegexMatch(self.pattern, targetString, regMatch, self.rawPointer) } }
これをより汎用的な形を目指そうとするなら、 Regex はクラスではなくインターフェースにするところだが、あくまでサンプルなのでこのままにする。 マッチさせた結果の RegexMatcher については、マッチングさせてエラーが発生した場合に Optional で返すのもちょっと使い勝手が悪かったので、何にもマッチしていない NoMatch という struct のオブジェクトを作って返すことにした。なお、 C で宣言した構造体については、 Swift にて拡張している。
実際のコードは次のような感じになる。
let target = "foobarbaz" for pattern in ["ba", "^foo", "[0-9]+", "o+b", "ab", "[abr]{2}"] { guard let regex = Regex(pattern: pattern) else { continue } print(regex) let matcher = regex.matcher(for: target) print(target, " matches? :", matcher.matches()) }
動かすと次のようなログが出力される。
$ swift run Sample
Compile CRegexp c_regexp.c
Compile Swift Module 'Sample' (2 sources)
Linking ./.build/x86_64-apple-macosx10.10/debug/Sample
Regular Expression : ba
foobarbaz matches? : true
Regular Expression : ^foo
foobarbaz matches? : true
Regular Expression : [0-9]+
foobarbaz matches? : false
Regular Expression : o+b
foobarbaz matches? : true
Regular Expression : ab
foobarbaz matches? : false
Regular Expression : [abr]{2}
foobarbaz matches? : true