Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
4
Help us understand the problem. What are the problem?

posted at

updated at

Swiftに組み込みの正規表現がやってくる…かも?

はじめに

これまでSwiftで正規表現というとNSRegularExpressionを使うという選択肢しかなかったかと思います。(…もしくはサードパーティ製ライブラリ?)

そこへきて、2021年9月末、Swift ForumsのEvolutionカテゴリに"Declarative String Processing Overview"というスレッドが立ち、一同騒然。
内容としては「2つの新しい“宣言可能な”文字列処理のAPIを導入したい」というもので、一つはPattern、もう一つがRegexです。Regexはもちろん正規表現のことです。
スレッドのauthorはMichael Ilseman氏。有名なところで言えば、Swift 5で文字列の内部保持データをUTF-16からUTF-8に刷新した立役者です1

そんな、SwiftのStringひいてはUnicode全般に精通している(と思われる)Ilseman氏の提案する文字列処理のAPIとは…?
この記事では正規表現(Regex)にスポットを当ててみましょう。

注意点

  • これらのAPIはまだ「議論しましょう」という段階で何ら正式な決定はされていません。
  • 私の個人的な意見としては、「“Swiftらしからぬ正規表現”を導入することには反対」という立場です(理由は当該スレッドの私のコメントを参照してください)。

Swiftで宣言できるようになる正規表現の案

では、正規表現の案を当該スレッドから引用してみましょう:

let regex = /([0-9A-F]+)(?:\.\.([0-9A-F]+))?\s*;\s(\w+).*/
print(type(of: regex))
// Prints Regex<(Substring, Substring?, Substring)>

PerlやRubyなどのスクリプト言語でよくみる形ですね。
でも、Swiftではいくつか問題になるであろう点が指摘されています。
そのうちのいくつかを紹介しましょう。

どうやってパースするの?

正規表現リテラルについて議論するための別スレッド"[Pitch] Regular Expression Literals"でも議論されていますが、/はもともとSwiftでよく使う記号です。従って、正規表現を書いたつもりが別の文や式として解釈される可能性があります。

たとえば空の正規表現//は、コメントの開始記号として解釈されることになってしまうでしょう。
さらにSwiftでは独自にoperatorを定義できるので、/hoge/((/hoge)/)(prefixとしての/が付いたhogeという識別子にsuffixとしての/がさらに付いている)とパースされる可能性があります。

というわけで、そこらへんをどうするの?という議論がなされています。
/を特別に扱うLexerをどう実装するかとか、そもそも/hoge/ではなく#/hoge/##regex(hoge)はどうだ、とか。

そもそも正規表現はSwiftに馴染むの?

正規表現の導入自体に反対する意見が少数なのは残念な限りですが、実際に反対意見は存在はします2。私も前述のとおりどちらかというと反対側の立場です。

そもそも正規表現の起源は1950年代で、UNIXで広まったのも1960年代と、かなり古びた技術です3

当初は、多言語対応などという概念すらおそらくなかったでしょう。ちなみに、PerlがUnicodeに対応したのは5.6(本格的には5.8)とつい最近4です。

Unicodeに対応した正規表現といっても、基本的には、書記素クラスタ(grapheme cluster)単位ではなく文字(character)単位というところに注意が必要です。

と、ここで混乱しないように用語の整理をしておきましょう。
Unicodeにおけるcharacterは、SwiftにおけるUnicode.Scalarのことです。文字だけでなく“文字の部品”も含む概念です。
SwiftにおけるCharacterは、Unicodeでいうところのgrapheme cluster(書記素クラスタ)で、1個以上のUnicode.Scalarからなる「人間から見た一文字」を表します。
即ち、UnicodeのcharacterとSwiftのCharacterは全くの別物というところは注意が必要です。
詳しくは、『[Swift] CharacterSetはCharacterのsetではありませんよ?』も読んでみてください。

ところで、SwiftのStringBidirectionalCollectionに準拠していますが、ElementCharacterであって、Unicode.Scalarではありません。
正規表現をSwiftで使おうと思った時に、そこが問題となってくるはずです。

Swiftの正規表現は「何」にマッチするの?

当該スレッドの私のコメントを元に考えてみましょう。

Character("A\u{20DD}")は、2個のUnicode.Scalar(LATIN CAPITAL LETTER A + COMBINING ENCLOSING CIRCLE)からなる1個のCharacterです。

そして

print((Character("A")...Character("B")).contains("A\u{20DD}"))
// -> true

というように、Swiftでは"A\u{20DD}""A"以上"B"以下の値と判定されます。

ここで/([A-B])/という正規表現を使って、"ZZA\u{20DD}ZZ"という文字列からマッチする「部分」を取り出すことを考えましょう。
Ilseman氏の案では次のような構文になります:

let regex = /([A-B])/

switch "ZZA\u{20DD}ZZ" {
case let something <- regex: // 正規表現のマッチする部分を`something`に代入する構文
  // `something`は何になる?
}

もし、CharacterCollectionとしてのStringにマッチさせたという解釈なら、somethingCharacter("A\u{20DD}")になるはずです。
しかし、往年のPerlプログラマ(?)にはその結果は違和感を抱かせることになるでしょう。Perlの正規表現はUnicodeのcharacter、即ちSwiftのUnicode.Scalar単位でマッチすることになるので、somethingUnicode.Scalar("A")であることを期待してしまうでしょう。

それでも「Swift的にはCharacterにマッチするんだよ!」と言ってくれれば、それはそれで「そういうものか…」とも思えるのですが、Ilseman氏は「Characterにマッチする方法もUnicode.Scalarにマッチする方法も選べるようにする」という旨の発言をしています。
どういうデザインになるか不明ですが、他の言語から入ってきたSwift初心者が混乱しそうな匂いがプンプンします…。

おわりに: 「僕たちの議論はまだ始まったばかりだ!」

最初にも述べたとおり、PatternRegexもまだ議論は始まったばかり。議論次第で、どうにでも転ぶ可能性があります。
少なくとも、東アジア圏の文字を無視してUnicodeを始めようとした失敗5と同様のことが、SwiftのRegexで起きないようにしていけるといいなと思います。

もしくは、今から議論に参加しておけば、自分好みの正規表現をSwiftで手に入れられる可能性も?!

Ilseman氏によればこれから関連するスレッドを細かく立てていく方針のようなので、自分の気になるスレッドだけでもチェックしておくといいかもしれません。

これからも素敵なSwift.Stringライフを送れますように!

●関連スレッド

概要

正規表現リテラルについて

「文字クラス」について

強く型付けされた正規表現のキャプチャについて


  1. [Swift] string.data(using: .utf8)ってnilになるの?」も読んでね。 

  2. https://forums.swift.org/t/pitch-regular-expression-literals/52820/3 とか https://forums.swift.org/t/pitch-regular-expression-literals/52820/63 とか 

  3. Wikipediaで得た浅い知識 

  4. 2000年〜2002年を「つい最近」と言っちゃうところがオジサン。 

  5. 彼奴等は16 bitで全世界の文字が収まると思ってたんだからびっくりだね。 

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
4
Help us understand the problem. What are the problem?