Skip to content

LISP - String Searching

Text processing is a common task in software development. Common Lisp provides powerful built-in tools for simple substring searches and can be extended with libraries for advanced pattern matching with regular expressions.

The primary function for finding a sequence within another is search. When used with strings, it finds a substring. It returns the starting index of the first match, or NIL if no match is found.

Section titled “Example: Case-Sensitive and Case-Insensitive Search”
(let ((main-string "Welcome to the Lisp tutorial!"))
(format t "Main String: '~a'~%~%" main-string)
;; 1. Successful case-sensitive search
(format t "Searching for \"Lisp\": ~a~%"
(search "Lisp" main-string))
;; 2. Failed case-sensitive search
(format t "Searching for \"lisp\": ~a~%"
(search "lisp" main-string))
;; 3. Successful case-insensitive search using :test
(format t "Searching for \"lisp\" (case-insensitive): ~a~%"
(search "lisp" main-string :test #'string-equal))
;; 4. Searching for a non-existent substring
(format t "Searching for \"Java\": ~a~%"
(search "Java" main-string)))
Main String: 'Welcome to the Lisp tutorial!'
Searching for "Lisp": 15
Searching for "lisp": NIL
Searching for "lisp" (case-insensitive): 15
Searching for "Java": NIL

The key to case-insensitivity is the :test keyword argument. By providing #'string-equal, we tell search to use a case-insensitive comparison function instead of the default #'eql.

Advanced Pattern Matching with Regular Expressions

Section titled “Advanced Pattern Matching with Regular Expressions”

For anything more complex than a simple substring, the industry standard is to use regular expressions (regex). While not built into the ANSI Common Lisp standard, a powerful library called cl-ppcre is widely used.

To use it, you first need a modern Lisp environment with the Quicklisp package manager installed. You can then load the library with:

;; In your Lisp REPL
(ql:quickload "cl-ppcre")

Imagine you want to find an email address inside a larger block of text. This is a perfect job for a regular expression.

;; Make sure you have loaded cl-ppcre first
(defpackage #:my-regex-example
(:use #:cl #:cl-ppcre))
(in-package #:my-regex-example)
(let ((text "Contact us at support@example.com for help."))
(format t "Text: '~a'~%" text)
;; The regex for a simple email address
(let ((email-regex "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"))
;; scan-to-strings returns the matched string as a result
(multiple-value-bind (match registers)
(scan-to-strings email-regex text)
(declare (ignore registers))
(if match
(format t "Found email address: ~a~%" match)
(format t "No email address found.~%")))))
Text: 'Contact us at support@example.com for help.'
Found email address: support@example.com

When to Use search vs. Regular Expressions

Section titled “When to Use search vs. Regular Expressions”
  • Use search when… you need to find a fixed, literal substring. It’s simple, fast, and built-in.
  • Use a regex library like cl-ppcre when… you need to find patterns (like emails, URLs, dates), validate input formats, or perform complex search-and-replace operations.