(defun split (s &optional (separators '(#\Space #\Tab #\Newline #\Return #\Page #\Vt)) (allow-empty-parts nil)) "This function splits S into parts. Each part is separated by one or more consecutive characters from the SEPARATORS list. Each part is appended to a list and returned to the caller. The ALLOW-EMPTY-PARTS boolean determines whether this function inserts empty strings to indicate that one separator directly followed another without any non-separator characters in the middle. If this boolean is set to NIL, any sequence of separators in S will be treated as just a single separator." (check-type separators list) (labels ;; This function is used by (reduce) to iterate over "s" in ;; reverse order. It accumulates the characters in "s" ;; producing a "char list list" where each "char list" will ;; become one of the split strings that gets returned. ((f (c acc) ;; If c is one of the separators, start a new list; ;; otherwise, append c to the first list in acc (which is the ;; inner (cons) call). We then have to replace the first ;; list on the accumulator with the new augmented list (which ;; is the outer (cons) call). (if (find c separators) (cons '() acc) (cons (cons c (car acc)) (cdr acc))))) ;; Split "s" into a "char list list" then use (coerce) to convert ;; the "char list" elements to strings. (let ((ss (mapcar (lambda (cs) (coerce cs 'string)) (reduce #'f s :initial-value '(()) :from-end t)))) ;; Return the split string as-is if allow-empty-parts is true; ;; otherwise, strip out the empty strings and return the ;; resulting list. (if allow-empty-parts ss (remove-if (lambda (s) (= (length s) 0)) ss)))))