common-lisp Comparison operators on characters and strings


Common Lisp has 12 type specific operators to compare two characters, 6 of them case sensitives and the others case insensitives. Their names have a simple pattern to make easy to remember their meaning:

Case SensitiveCase Insensitive

Two characters of the same case are in the same order as the corresponding codes obtained by CHAR-CODE, while for case insensitive comparisons the relative order between any two characters taken from the two ranges a..z, A..Z is implementation dependent. Examples:

(char= #\a #\a)
T ;; => the operands are the same character
(char= #\a #\A)
NIL ;; => case sensitive equality
(CHAR-EQUAL #\a #\A)
T ;; => case insensitive equality
(char> #\b #\a)
T ;; => since in all encodings (CHAR-CODE #\b) is always greater than (CHAR-CODE #\a)
(char-greaterp #\b \#A)
T ;; => since for case insensitive the ordering is such that A=a, B=b, and so on,
  ;;    and furthermore either 9<A or Z<0.
(char> #\b #\A)
?? ;; => the result is implementation dependent

For strings the specific operators are STRING=, STRING-EQUAL, etc. with the word STRING instead of CHAR. Two strings are equal if they have the same number of characters and the correspending characters are equal according to CHAR= or CHAR-EQUAL if the test is case sensitive or not.

The ordering between strings is tje lexicographic order on the characters of the two strings. When an ordering comparison succeeds, the result is not T, but the index of the first character in which the two strings differ (which is equivalent to true, since every non-NIL object is a “generalized boolean” in Common Lisp).

An important thing is that all the comparison operators on string accept four keywords parameters: start1, end1, start2, end2, that can be used to restrict the comparison to only a contiguous run of characters inside one or both strings. The start index if omitted is 0, the end index is omitted is equal to the length of the string, and the comparison in performed on the substring starting at character with index :start and terminating with the character with index :end - 1 included.

Finally, note that a string, even with a single character, cannot be compared to a character.


(string= "foo" "foo")
T ;; => both strings have the same lenght and the characters are `CHAR=` in order
(string= "Foo" "foo")
NIL ;; => case sensitive comparison
(string-equal "Foo" "foo")
T ;; => case insensitive comparison
(string= "foobar" "barfoo" :end1 3 :start2 3)
T ;; => the comparison is perform on substrings
(string< "fooarr" "foobar")
3 ;; => the first string is lexicographically less than the second one and 
  ;;   the first character different in the two string has index 3
(string< "foo" "foobar")
3 ;; => the first string is a prefix of the second and the result is its length

As a special case, the string comparison operators can also be applied to symbols, and the comparison is made on the SYMBOL-NAME of the symbol. For instance:

(string= 'a "A")
T ;; since (SYMBOL-NAME 'a) is "A"
(string-equal '|a| 'a)
T ;; since the the symbol names are "a" and "A" respectively

As final note, EQL on characters is equivalent to CHAR=; EQUAL on strings is equivalent to STRING=, while EQUALP on strings is equivalent to STRING-EQUAL.