[iOS][Swift]長いパターンの正規表現を扱う方法

2016.10.13

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

おばんです、人生初札幌上陸で、寒さからテンション上がってる田中です。
寒い!空気がうまい!気温と生産性は反比例の関係にあると思ってる!

今回はiOS/Swiftで長いパターンの正規表現を扱う方法について。

結論

長いパターンの正規表現をiOS/Swiftで扱う場合には直接文字列をソースコード上に展開するのではなく、外部ファイル化してそのファイルの文字列を参照して扱うようにしましょうというお話。

短いパターンの正規表現を扱う場合

長いパターンを扱う話の前に、一旦短いパターンの話をします。

例えばSwiftの例であればEmail Address Regular Expression That 99.99% Works.というサイトによると以下のようなRFC5322形式のメールアドレスの正規表現パターンの例があります。

// Swift (Regex only)
[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}

実際に扱うとすれば以下のようなコードになります。

// Swift (Regex used in a function)
func validateEmail(candidate: String) -> Bool {
 let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}"
 return NSPredicate(format: "SELF MATCHES %@", emailRegex).evaluateWithObject(candidate)
}

左から順に展開すると

  • 大文字小文字英数字と._%+-
  • 大文字小文字英数字.-
  • .を一つ
  • 末尾に2文字から6文字の大文字小文字英数字

というパターンで引数に与えられた文字列がメールアドレスであるかどうかを判定しています。
いわゆるメールアドレスのバリデーションを行っています。

長いパターンの正規表現を扱う場合

さてRFC5322形式であれば先述したやり方で問題ないでしょう。
しかし世の中にはもっと長い形式の正規表現も存在するわけで、例えばRFC822形式のメールアドレスのバリデーションなどがありまして。
長さを知りたい方は以下をご覧ください。
Mail::RFC822::Address Regex - Stack Overflow

Playgroundで実際にこのパターンとして直接文字列で指定しようとすると以下のように文字列として判別してくれません。

スクリーンショット 2016-10-13 0.47.16

目が痛い...。
うまく考えて特殊文字をエスケープしていくなんて一苦労、とてもじゃないですができません...。
そこで冒頭で述べた方法をとります。

正規表現のパターンを外部ファイル化する

メモ帳でもなんでも構いません、上で挙げた文字列をそのままコピペして.txtなどのファイル形式で保存します。

作成した外部ファイルからバリデーションの関数を作る

以下の通り。

func validateEmail(email: String?) -> Bool {
    let path = NSBundle.mainBundle().pathForResource("EmailRegexPattern", ofType: "txt")
    let fileHandle = NSFileHandle(forReadingAtPath: path!)
    let data = fileHandle!.readDataToEndOfFile()
    
    let emailRegex = String(data: data, encoding: NSUTF8StringEncoding)
    
    return NSPredicate(format: "SELF MATCHES %@", emailRegex!).evaluateWithObject(email)
}

ね、簡単でしょう?

まとめ

長い正規表現のパターンを実装することになっても焦る必要はありません。
外部ファイル化です。外部ファイル化するのです。