lispとrubyとpythonと その6 正規表現(common lisp)

Common Lispでの正規表現
cl-ppcreを使う。cl-interpolを入れとくと正規表現リテラルが使えるので、これも入れといた方がよい。

;;正規表現はcl-ppcreで。
;;asdfでインストールしておく
;;(require 'asdf)
;;(require 'asdf-install)
;;(asdf-install:install 'cl-ppcre)

(require 'cl-ppcre)
;;正規表現リテラルを使いたいからcl-interpol使う
;;(cl-interpol:enable-interpol-syntax)すれば正規表現リテラルが使える
;;cl-ppcreではマッチしなかった時大抵はnilが返る

(require 'cl-interpol)
(cl-interpol:enable-interpol-syntax)

(setf target "http://faroffsea.blogspot.com/")
(setf scanner (cl-ppcre:create-scanner 
   #?/(.*):\/\/(.*)\//
   :case-insensitive-mode t
   :multi-line-mode nil))

;;scan
;;戻り値はマッチ開始位置、マッチ終了位置、マッチレジスタ開始位置のリスト、マッチレジスタ終了位置のリスト
;;(cl-ppcre:scan scanner target);;でも一緒
(cl-ppcre:scan #?/(.*):\/\/(.*)\// target)
;;=>0
;;  30
;;  #(0 7)
;;  #(4 29)


;;scanの戻り値を格納するバージョン
(cl-ppcre:do-scans (s         ;開始位置を格納する変数
      e         ;終了位置を格納する変数
      rs        ;マッチレジスタ開始位置のリストを格納する変数
      re        ;マッチレジスタ終了位置のリストを格納する変数
      scanner   ;正規表現
      target)   ;ターゲット文字列
  (format t "match-start:~A~%" s)
  (format t "match-end:~A~%" e)
  (format t "reg-start:~A~%" rs)
  (format t "reg-ends:~A~%" re))
;;->match-start:0
;;  match-end:30
;;  reg-start:#(0 7)
;;  reg-ends:#(4 29)

;;do-scansのreg-start、reg-endsなし版
(cl-ppcre:do-matches (s
        e
        #?/aaa/
        "aaa abc aaabc cccaaabbb")
  (format t "match-start:~A~%" s)
  (format t "match-end:~A~%" e))
;;->match-start:0
;;  match-end:3
;;  match-start:8
;;  match-end:11
;;  match-start:17
;;  match-end:20

;;位置から自分で切り出すのは面倒だから変数にバインドしてくれるregister-groups-bindかdo-register-groupsを使う
(cl-ppcre:register-groups-bind (protocol domain)
    (#?/(.*):\/\/(.*)\// target :sharedp t)
  (format t "protocol:~A~%domain:~A~%" protocol domain))
;;->protocol:http
;;  domain:faroffsea.blogspot.com

(cl-ppcre:register-groups-bind (protocol domain)
    (scanner target :sharedp t)
  (format t "protocol:~A~%domain:~A~%" protocol domain))
;;->protocol:http
;;  domain:faroffsea.blogspot.com

(cl-ppcre:do-register-groups (protocol domain)
    (#?/(.*):\/\/(.*)\// target)
  (format t "protocol:~A~%domain:~A~%" protocol domain))
;;->protocol:http
;;  domain:faroffsea.blogspot.com

(cl-ppcre:do-register-groups (protocol domain)
    (scanner target)
  (format t "protocol:~A~%domain:~A~%" protocol domain))
;;->protocol:http
;;  domain:faroffsea.blogspot.com

;;こんなことも可能
;;var-list指定で(#'parse-integer yyyy)としておくと数値に変換して変数に入れてくれる
(cl-ppcre:register-groups-bind ((#'parse-integer yyyy) (#'parse-integer mm) (#'parse-integer dd))
    (#?/([0-9]+)\/([0-9]+)\/([0-9]+)/ "2008/12/31/" :sharedp t)
    (list yyyy mm dd))
;;=>(2008 12 31)

;;サクッと書くならscan-to-strings
(cl-ppcre:scan-to-strings scanner target)
;;->"http://faroffsea.blogspot.com/"
;;  #("http" "faroffsea.blogspot.com")

;;サクッと書くならall-matches
;;(開始位置 終了位置 開始位置 終了位置...)のplistを返す
(cl-ppcre:all-matches #?/aaa/ "aaa abc aaabc cccaaabbb")
;;=>(0 3 8 11 17 20)

;;サクッと書くならall-matches-as-strings
;;マッチした文字列のリストが返る
(cl-ppcre:all-matches-as-strings #?/aaa/ "aaa abc aaabc cccaaabbb")
;;=>("aaa" "aaa" "aaa")

;;named capture
;;*allow-named-registers*をtにしないと使えない
;;nameでキャプチャ文字列を引っ張ってくることができるわけじゃないみたい
;;create-scannerした時の2番目の戻り値を覚えておいてあとで自分でnameに対応するキャプチャ文字を取得するのか?
(setf cl-ppcre:*allow-named-registers* t)

(cl-ppcre:create-scanner #?/(?<c0>.*):\/\/(?<c1>.*)\//
    :case-insensitive-mode t
    :multi-line-mode nil)
;;=>#<CLOSURE (LAMBDA (STRING CL-PPCRE::START CL-PPCRE::END)) {C3B738D}>
;;  ("c0" "c1")

;;split
;;文字列分割
(cl-ppcre:split #?/\t/ #?"aaa\tbbb\tccc")
;;=>("aaa" "bbb" "ccc")

;;文字列置換
(cl-ppcre:regex-replace #?/aaa/ "aaa abc aaabc cccaaabbb" "xxx" )

(let ((scanner (cl-ppcre:create-scanner #?/(aaa)/)))
  (cl-ppcre:regex-replace scanner  "aaa abc aaabc cccaaabbb" #?/\1_change/))
;;=>"aaa_change abc aaabc cccaaabbb"