CL PPCRE - lisp-cookbook-ja/common-lisp GitHub Wiki

正規表現 cl-ppcre

CLiki:CL-PPCREでは正規表現を文字列かS式で表現します。文字列で正規表現を表現する場合は、文字クラスを使うときなど、Perlなどで使われる表記をエスケープしなければならないことがあります。

導入

Quicklispから入れることができます。

(ql:quickload :cl-ppcre)

パターンの例

基本的にPerlの正規表現を文字列で渡せば問題ありません。ただし、上にもありますが、文字列で正規表現を表す場合、表記をエスケープしなければならないことがあります。

;; bにマッチする
(ppcre:scan "b" "abc")
(ppcre:scan #\b "abc")
;=> 1
;   2
;   #()
;   #()

;; 任意の文字にマッチする
(ppcre:scan "." "abc")
(ppcre:scan :everything "abc")
;=> 0
;   1
;   #()
;   #()

;; 任意の3文字にマッチする
(ppcre:scan "..." "abc")
(ppcre:scan '(:sequence :everything :everything :everything) "abc")
;=> 0
;   3
;   #()
;   #()

;; Perlの文字クラス\wにマッチする
;; "\w"は"w"と同じ意味になってしまうため、"\\w"としなければならない
(ppcre:scan "\\w" "abc")
(ppcre:scan :word-char-class "abc")
;=> 0
;   1
;   #()
;   #()

;; aとbとcのいずれかにマッチする
(ppcre:scan "[abc]" "a")
(ppcre:scan '(:char-class #\a #\b #\c) "a")
;=> 0
;   1
;   #()
;   #()
(ppcre:scan "[abc]" "c")
(ppcre:scan '(:char-class #\a #\b #\c) "c")
;=> 0
;   1
;   #()
;   #()

;; aとbとc以外にマッチする
(ppcre:scan "[^abc]" "c")
(ppcre:scan '(:inverted-char-class #\a #\b #\c) "c")
;=> NIL
(ppcre:scan "[^abc]" "d")
(ppcre:scan '(:inverted-char-class #\a #\b #\c) "d")
;=> 0
;   1
;   #()
;   #()

;; 行の最初のaにマッチする
(ppcre:scan "^a" "abc")
(ppcre:scan '(:sequence :start-anchor #\a) "abc")
;=> 0
;   1
;   #()
;   #()
(ppcre:scan "^a" "cba")
(ppcre:scan '(:sequence :start-anchor #\a) "cba")
;=> NIL

;; 行の最後のcにマッチする
(ppcre:scan "c$" "abc")
(ppcre:scan '(:sequence #\c :end-anchor) "abc")
;=> 2
;   3
;   #()
;   #()
(ppcre:scan "c$" "aaa")
(ppcre:scan '(:sequence #\c :end-anchor) "aaa")
;=> NIL

;; 0文字以上の数字にマッチする
(ppcre:scan "\\d*" "01234")
(ppcre:scan '(:greedy-repetition 0 nil :digit-class) "01234")
;=> 0
;   5
;   #()
;   #()

;; 1文字以上の数字にマッチする
(ppcre:scan "\\d+" "01234")
(ppcre:scan '(:greedy-repetition 1 nil :digit-class) "01234")
;=> 0
;   5
;   #()
;   #()
(ppcre:scan "\\d+" "abc012")
(ppcre:scan '(:greedy-repetition 1 nil :digit-class) "abc012")
;=> 3
;   6
;   #()
;   #()

メタ文字自体へのマッチ

文字列で表した正規表現でメタ文字自体へマッチさせたい場合、メタ文字をエスケープしてパターンに指定しなければなりません。

;; 「^」にマッチする
(ppcre:scan "\\^" "^")
(ppcre:scan #\^ "^")
;=> 0
;   1
;   #()
;   #()

;; 「^」でありがちなミス(行の先頭にマッチ)
(ppcre:scan "^" "^")
;=> 0
;   0
;   #()
;   #()

;; 応用例:指数を取り出す
(ppcre:register-groups-bind (e)
    ("\\^([0-9]+)=" "2^10=1024")
  e)
(ppcre:register-groups-bind (e)
    ('(:sequence
       #\^
       (:register (:greedy-repetition 1 nil (:char-class (:range #\0 #\9))))
       #\=)
      "2^10=1024")
  e)
;=> "10"

;; 「(」や「)」にマッチする
(ppcre:scan "\\(" "(")
(ppcre:scan #\( "(")
(ppcre:scan "\\)" ")")
(ppcre:scan #\) ")")
;=> 0
;   1
;   #()
;   #()

;; 「(」や「)」でありがちなミス(正規表現の構文エラー)
(ppcre:scan "(" "(")
(ppcre:scan ")" ")")
;>> Error: Expected end of string.

;; 応用例:括弧内の文字を取り出す
(ppcre:register-groups-bind (n)
    ("\\(([0-9]+)\\)$" "math(85)")
  n)
(ppcre:register-groups-bind (n)
    ('(:sequence
       #\(
       (:register (:greedy-repetition 1 nil (:char-class (:range #\0 #\9))))
       #\)
       :end-anchor)
      "math(85)")
  n)
;=> "85"

パターンマッチ

;; マッチした位置を返す
(ppcre:scan "[c-e]+" "abcdefg")
;=> 2
;   5
;   #()
;   #()

;; 三番目と四番目の戻り値はグループ化されたパターンがマッチした位置
(ppcre:scan "((a)(b)(c))" "abc")
;=> 0
;   3
;   #(0 0 1 2)
;   #(3 1 2 3)

;; マッチする部分がないときはNILが返る
(ppcre:scan "a" "b")
;=> NIL

;; マッチした部分文字列を返す
(ppcre:scan-to-strings "\\d+" "Originally specified in 1958, Lisp is")
;=> "1958"
;   #()

;; パターンにマッチする全ての部分文字列をリストで返す
(ppcre:all-matches-as-strings "\\w+" "foo bar baz")
;=> ("foo" "bar" "baz")

変数への束縛

パターンにマッチした文字列を変数に束縛します。

;; グループ化されたパターンがマッチした部分の文字列を順番に束縛する
(ppcre:register-groups-bind (base _ exp)
    ("(\\d+)(\\^(\\d+))?" "10^1000")
  (declare (ignore _))
  (values (read-from-string base)
          (read-from-string exp)))
;=> 10
;   1000

;; グループ化されたパターンがないとエラー
(ppcre:register-groups-bind (match)
    ("a" "abc")
  match)
;>> Error: Array index 0 out of bounds for #() .

繰り返し

パターンにマッチする部分が見付かるごとに処理を繰り返します。

;; マッチした部分の位置が渡される
(let ((target "1 20 300 4000 50000"))
  (ppcre:do-matches (start end "\\d+" target)
    (format t "~A~%" (subseq target start end))))
;-> 1
;   20
;   300
;   4000
;   50000
;=> NIL

;; マッチした部分の文字列が渡される
(ppcre:do-matches-as-strings (match "\\d+" "1 20 300 4000 50000")
  (format t "~A~%" match))
;-> 1
;   20
;   300
;   4000
;   50000
;=> NIL

パターンの置換

;; 置換後の文字列とマッチしたかどうかを返す
(ppcre:regex-replace "<" "<<<" "&lt;")
;=> "&lt;<<"
;   T

;; マッチしなければ二番目の戻り値はNIL
(ppcre:regex-replace ">" "<<<" "&lt;")
;=> "<<<"
;   NIL

;; マッチするパターンすべてを置き換える
(ppcre:regex-replace-all "<" "<<<" "&lt;")
;=> "&lt;&lt;&lt;"
;   T

;; 応用例:改行を空白に置換する
(ppcre:regex-replace-all "\\n" "a
b
c" " ")
(ppcre:regex-replace-all #\newline "a
b
c" " ")
;=> "a b c"
;   T

パターンによる分割

;; 分割してリストで返す
(ppcre:split "," "a,b,c")
;=> ("a" "b" "c")

;; マッチしたパターンが文字列の最後にあるときは無視される
(ppcre:split "," "a,b,c,")
;=> ("a" "b" "c")

;; マッチしないときはそのままの文字列をリストにして返す
(ppcre:split "," "abc")
;=> ("abc")

参考文献