Substrings

In Common Lisp this function is called subseq, in other languages it is usually called substr.

(subseq string start end)

(defvar str "Hello World")
(subseq str 6) 
Output:
"World"
Try:
(subseq str 3 8) 
Output:
"lo Wo"

While you can “overwrite” a subsequence of a string, that string will not be “stretched” to fit your new subsequence should it prove longer.

(setf (subseq str 0 5) "Goodbye Cruel") 
Output:
"Goodb World"

Notice it is not what we expected, i.e. it did not automatically expand the string to “Goobye Cruel World”.

Individual Characters

This function is called char, other languages often supply syntactic sugar that allows you to
reference strings like you would an array with the [] operator.

(char string index)

(defvar str "Hello World")
(char str 0)
(aref str 0)
(elt str 0)
Output:
#\H
#\H
#\H

Because strings are implemented as arrays, it is possible to also use aref and elt.

You can set a character directly with setf.

(setf (char str 0) #\J)

Result:

"Jello World"



Chars are not very surprising in Lisp, you can access UTF-8 Character Codes with code-char

(code-char integer)

(code-char 666)
Result:
#\LATIN_SMALL_LETTER_CLOSED_OPEN_E

A string is treated as a sequence, and so any sequence function can act on it.

(remove char string)

We can remove some arbitrary char, or chars with remove
(defvar str "Hello World")
(remove #\o str) 
Result:
"Hell Wrld"

(remove-if predicate string)

We can remove some arbitrary char, or chars with remove-if by specifying a predicate function to run on each character.
(defvar str "Hello World")
(remove-if #'lower-case-p str)
(remove-if #'upper-case-p str)
(remove-if #'(lambda (x) (if (eq x #\e) t nil)) str)
Result:
"H W" 
"ello orld"
"Hllo World"

(substitute replacement needle haystack)

We can susbstitue some arbitrary char, or chars with substitute
(defvar str "Hello World")
(substitute #\u #\e str)
Result:
"Hullo World"

(substitute-if replacement predicate haystack)

Or we can substitute-if and use a predicate.
(defvar str "Hello World")
(substitute-if #\o #'lower-case-p str)
Result:
"Hoooo Woooo"

(find char str :test function)

We can search for a char in a string with find.
(find #\e "Hello" :test #'equalp)
(find #\z "Hello" :test #'equalp)
Result:
#\e
NIL

Snake Case

We can use these functions to implement snake_case:

(defvar str "Something Wicked This Way Comes")
(substitute #\_ #\Space str)
=> "Something_Wicked_This_way_Comes"

Or we can get a bit more fancy:

(defvar str "Something Wicked This Way Comes")
(defun snake-case (x)
  (string-downcase (substitute #\_ #\Space str)))
(snake-case str)
=> "something_wicked_this_way_comes"

Even that is not good enough to snake-case complex strings, so we can do this:


(defun snake-case (str)
  (let ( ( result "") )
    (loop for i from 0 to (- (length str) 1) do
      (if (>= i (- (length str) 1))
        (setf result (format nil "~a~C" result (char str i) ))
        (let ((nxt (char str (+ 1 i))) (current (char str i)))
          (cond
              ((and
                (not (eq nxt (char-downcase nxt)))
                (not (and (eq current #\Space) (eq current #\_))))
                ; then this is some mark + Capital, insert a space
                (if (eq current #\_)
                  (setf result (format nil "~a~C" result current ))
                  (setf result (format nil "~a~C_" result current ))))
              (t
                (setf result (format nil "~a~C" result current)))))))
  (string-downcase (substitute #\_ #\Space result))))
Try:
(print (snake-case "hello world"))
(print (snake-case "HelloWorld"))
(print (snake-case "_HelloWorldD2"))
Result:
"hello_world"
"hello_world"
"_hello_world_d2"

Split

Users of other programming languages will generally expect a way to split a given string
by another string, not necessarily a single character. There is no standard way to do this,
but here is a function that will do it.

(defun split (delim str &rest values)
  ;(print (format t "SPLIT: ~{ ~a ~} ~C" values #\return))
  ; If it's our first pass, just recurse with default values
  (cond ((eq values nil) (split delim str 0 0 '()))
        ; If we don't have enough characters left in the string,
        ; append what's left and return
        ((> (car values) (- (length str) (length delim)))
          (append
            (caddr values)
            (list (subseq str (cadr values) (length str))) ))
        ; else
        (t
          (let (( part (subseq str (car values) (+ (car values) (length delim))))
                  ( offset (car values))
                  ( from (cadr values))
                  ( results (caddr values) ) )
            ; Is the current part of the string equal to the delim?
            (cond ((equal part delim)
                  (let (( addon (subseq str from offset)))
                  ; Recurse after appending everything up to the delim
                  ; set the offset to offest + delim length
                  ; set the new from to the offset + delim length
                  ; append the results and boom
                  (split
                      delim
                      str
                      (+ offset (length delim))
                      (+ offset (length delim))
                      (append results (list addon) ))))
                  ; else advance one character and try again!
                  (t (split delim str (+ offset 1) from results)))))))