Skip to content

LISP - Accessing Characters of String

In Common Lisp, a string is a one-dimensional array of characters. This structure allows for efficient, direct access to any character by its index. There are several standard functions to perform this access, each with subtle differences in usage and performance.

Key points to remember:

  • Zero-Based Indexing: The first character is at index 0, the second at index 1, and so on.
  • Character Type: The functions return a value of type character, which Lisp prints using the #\ notation (e.g., #\A).
FunctionDescriptionBest For
charThe most general and recommended function. It works on any kind of string.General purpose string access.
scharA specialized version of char that only works on simple-strings. It can be faster because it avoids checks required for more complex strings (e.g., those with fill pointers).Performance-critical code where you know the string is a simple-string.
arefThe generic array reference function. Since strings are arrays, it can be used to access characters. It is functionally equivalent to char for this purpose.Generic code that operates on any type of array, not just strings.

The following example demonstrates how to use all three functions to access characters from a string. Note that for a standard string literal, their behavior is identical.

;; Use defparameter for a global, dynamic variable (a common convention).
(defparameter *my-string* "Modern Lisp")
(format t "Original String: ~s~%~%" *my-string*)
;; --- Accessing the first character (index 0) ---
(format t "Using (char *my-string* 0): ~c~%" (char *my-string* 0))
(format t "Using (schar *my-string* 0): ~c~%" (schar *my-string* 0))
(format t "Using (aref *my-string* 0): ~c~%~%" (aref *my-string* 0))
;; --- Accessing the seventh character (index 6) ---
(let ((index 6))
(format t "Accessing character at index ~d:~%" index)
(format t "Character is: ~c~%" (char *my-string* index)))
Original String: "Modern Lisp"
Using (char *my-string* 0): M
Using (schar *my-string* 0): M
Using (aref *my-string* 0): M
Accessing character at index 6:
Character is: L
  • Off-by-One Error: Forgetting that indexing starts at 0 is a common mistake. The last character of a string s is at index (- (length s) 1).
  • Index Out of Bounds: Attempting to access an index equal to or greater than the string’s length will signal a runtime error. Always ensure your index is within the valid range [0, length-1].
  • Type Error with schar: If you use schar on a complex string (e.g., one created with a fill pointer), a type-error will be signaled. Stick to char unless you have a specific performance reason and have verified the string type.
;; Example of an out-of-bounds error
(let ((my-string "Hi"))
;; This will cause an error because the valid indices are 0 and 1.
(char my-string 2))

For clarity, robustness, and general-purpose code, prefer char. The potential performance gain from schar is often negligible in modern Lisp implementations unless you are operating inside a tight loop over millions of characters. Use aref when writing code that is designed to work with arrays in general, not just strings.