CL PPCRE - lisp-cookbook-ja/common-lisp GitHub Wiki
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 "<" "<<<" "<")
;=> "<<<"
; T
;; マッチしなければ二番目の戻り値はNIL
(ppcre:regex-replace ">" "<<<" "<")
;=> "<<<"
; NIL
;; マッチするパターンすべてを置き換える
(ppcre:regex-replace-all "<" "<<<" "<")
;=> "<<<"
; 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")