← Blog of oldlisper

cookbook:regular-expressions

· 24.09.2009 11:56
· old wiki

Регулярные выражения

Как можно использовать регулярные выражения?

Хотя реализации не предоставляют такой возможности, существует хорошая библиотека CL-PPCRE оптимизированная по скорости, и позволяющая использовать регулярные выражения, как они используются в Perl.

CL-PPCRE ("Common Lisp Portable Perl Compatible Regular Expression") отличают следующие свойства:

  1. Сами регулярные выражения совместимы с Перл-овыми.
  2. Библиотека работает для разных ANSI реализаций CL.
  3. Есть альтернативная запись регулярных выражений в виде s-выражений.
  4. Работает быстро, как было сказано.
  5. Библиотека свободна.

Про установку написано выше, в часте про ASDF.

Примеры использования:

(scan "b.r" "foo bar baz bur" :start 2 :end 10)
; вернёт 5 и 7

функция scan ищет во второй строке начиная с индекса start и до индекса end совпадения с RE, определённым в первой строке, при отсутствии совпадений возвращает NIL, иначе - индексы начала и конца первого совпадения.

Допустим стоит задача переводить текст между \=\=\=\=, \=\=\= и \=\= в html заголовки h1, h2 и h3 соответственно. Этого можно достичь трёмя строчка - по одной на каждую задачу:

(setf *str* (regex-replace-all "\=\=\=\=(.*)=\=\=\=\=" *str* "<h1>\\1</h1>" :preserve-case t))
(setf *str* (regex-replace-all "\=\=\=(.*)\=\=\=" *str* "<h2>\\1</h2>" :preserve-case t))
(setf *str* (regex-replace-all "\=\=(.*)\=\=" *str* "<h3>\\1</h3>" :preserve-case t))
; Как видим, перловые $1, $2, $3, ... для запоминания паттернов в скобках () заменяются переменными \\1, \\2, \\3, ...

Вот тут FIXME , я этот код не проверял.

Перевод объявлений #define из .h файлов

(defun h-to-lisp (f.h f.lisp)
  "Перевод однострочных объявлений #define из .h файлов"
    (let ((in (open f.h :if-does-not-exist nil)) (out (open f.lisp :direction :output :if-exists :supersede)))
        (when in
            (loop for line = (read-line in nil)
                while line do (if (scan "#define\\s+\\S+\\s+\\S+" line)
                                  (format out "~a~%"
                                              (string-downcase  ;; уменьшаем регистр.
                                               (substitute #\- #\_  ;; Вместо _ ставим - .
                                                 (regex-replace "#\\s*define\\s+(\\S+)\\s+(\\S+).*" line "(defconstant +\\1+ \\2)")
)
)
)
)
)

            (close in)
            (close out)
)
)
)

Может оказаться, что вторым значением в #define будет 8 или 16-ричное число, которое записывается в Си и в CL по-разному, поэтому можно встроить в h-to-lisp такую функцию:

(defun parse-c-token (token)
  (cond ((scan "0+[0-9]+"     token) (read-from-string (regex-replace "0+([0-9]+)"     token "#o\\1")))
        ((scan "0x[0-9|a-f]+" token) (read-from-string (regex-replace "0x([0-9|a-f]+)" token "#x\\1")))
        ((scan "[0-9]+l"      token) (read-from-string (regex-replace "([0-9|\.]+)l"   token "\\1")))
        ((scan "'\\S+'"       token) (read-from-string (regex-replace "'(\\S+)'"       token "#\\\1")))
        (t                           token)
)
)

1)

Библиотеки

CL-PPCRE

Другие библиотеки, реализующие работу с регулярными выражениями: ...

1)ну, примерно так - могут быть баги.