Strings are sequences of characters. The length of a string is
the number of characters that it contains, as an exact non-negative integer.
This number is usually fixed when the string is created,
however, you can extend a mutable string with
The valid indices of a string are the
exact non-negative integers less than the length of the string.
The first character of a
string has index 0, the second has index 1, and so on.
Strings are implemented as a sequence of 16-bit
even though they’re semantically a sequence of 32-bit Unicode code points.
A character whose value is greater than
is represented using two surrogate characters.
The implementation allows for natural interoperability with Java APIs.
However it does make certain operations (indexing or counting based on
character counts) difficult to implement efficiently. Luckily one
rarely needs to index or count based on character counts;
alternatives are discussed below.
Some of the procedures that operate on strings ignore the
difference between upper and lower case. The names of
the versions that ignore case end with “
-ci” (for “case
The type of string objects. The underlying type is the interface
java.lang.CharSequence. Immultable strings are
java.lang.String, while mutable strings are
objis a string,
Return a newly allocated string composed of the arguments. This is analogous to
Return a newly allocated string of length
charis given, then all elements of the string are initialized to
char, otherwise the contents of the
Return the number of characters in the given
stringas an exact integer object.
Performance note: Calling
string-lengthmay take time proportional to the length of the
string, because of the need to scan for surrogate pairs.
kmust be a valid index of
string-refprocedure returns character
stringusing zero–origin indexing.
Performance note: Calling
string-refmay take time proportional to
kbecause of the need to check for surrogate pairs. An alternative is to use
string-cursor-ref. If iterating through a string, use
This procedure stores
string.(define s1 (make-string 3 #\*)) (define s2 "***") (string-set! s1 0 #\?) ⇒ void s1 ⇒ "?**" (string-set! s2 0 #\?) ⇒ error (string-set! (symbol->string 'immutable) 0 #\?) ⇒ error
Performance note: Calling
string-set!may take time proportional to the length of the string: First it must scan for the right position, like
string-refdoes. Then if the new character requires using a surrogate pair (and the old one doesn’t) then we have to make room in the string, possibly re-allocating a new
chararray. Alternatively, if the old character requires using a surrogate pair (and the new one doesn’t) then following characters need to be moved.
string-set!is deprecated: It is inefficient, and it very seldom does the correct thing. Instead, you can construct a string with
stringmust be a string, and
endmust be exact integer objects satisfying:0 <=
substringprocedure returns a newly allocated string formed from the characters of
stringbeginning with index
start(inclusive) and ending with index
Return a newly allocated string whose characters form the concatenation of the given strings.
stringmust be a mutable string, such as one returned by
stringby appending each
value(in order) to the end of
valueshould be a character or a string.
Performance note: The compiler converts a call with multiple
values to multiple
string-append!calls. If a
valueis known to be a
character, then no boxing (object-allocation) is needed.
The following example shows how to efficiently process a string using
string-for-eachand incrementally “build” a result string using
string-append!.(define (translate-space-to-newline str::string)::string (let ((result (make-string 0))) (string-for-each (lambda (ch) (string-append! result (if (char=? ch #\Space) #\Newline ch))) str) result))
It is an error if any element of
listis not a character.
string->listprocedure returns a newly allocated list of the characters of
list->stringprocedure returns a newly allocated string formed from the characters in
list. In both procedures, order is preserved. The
list->stringprocedures are inverses so far as
strings must all have the same length.
procshould accept as many arguments as there are
endvariant is provided for compatibility with the SRFI-13 version. (In that case
endcount code Unicode scalar values (
charactervalues), not Java 16-bit
procelement–wise to the characters of the
strings for its side effects, in order from the first characters to the last.
procis always called in the same dynamic environment as
for-each.(let ((v '())) (string-for-each (lambda (c) (set! v (cons (char->integer c) v))) "abcde") v) ⇒ (101 100 99 98 97)
Performance note: The compiler generates efficient code for
procis a lambda expression, it is inlined.
procelement-wise to the elements of the strings and returns a string of the results, in order. It is an error if
procdoes not accept as many arguments as there are strings, or return other than a single character. If more than one string is given and not all strings have the same length,
string-mapterminates when the shortest string runs out. The dynamic order in which
procis applied to the elements of the strings is unspecified.(string-map char-foldcase "AbdEgH") ⇒ "abdegh"(string-map (lambda (c) (integer->char (+ 1 (char->integer c)))) "HAL") ⇒ "IBM"(string-map (lambda (c k) ((if (eqv? k #\u) char-upcase char-downcase) c)) "studlycaps xxx" "ululululul") ⇒ "StUdLyCaPs"
Performance note: The
string-mapprocedure has not been optimized (mainly because it is not very useful): The characters are boxed, and the
procis not inlined even if it is a lambda expression.
Returns a newly allocated copy of the part of the given
Replaces the characters of string
dst-end) with the characters of
src-end). The number of characters from
srcmay be different than the number replaced in
dst, so the string may grow or contract. The special case where
dst-startis equal to
dst-endcorresponds to insertion; the case where
src-startis equal to
src-endcorresponds to deletion. The order in which characters are copied is unspecified, except that if the source and destination overlap, copying takes place as if the source is first copied into a temporary string and then into the destination. (This is achieved without allocating storage by making sure to copy in the correct direction in such circumstances.)
Copies the characters of the string
fromthat are between
endinto the string
to, starting at index
at. The order in which characters are copied is unspecified, except that if the source and destination overlap, copying takes place as if the source is first copied into a temporary string and then into the destination. (This is achieved without allocating storage by making sure to copy in the correct direction in such circumstances.)
This is equivalent to (and implemented as):(string-replace! to at (+ at (- end start)) from start end))(define a "12345") (define b (string-copy "abcde")) (string-copy! b 1 a 0 2) b ⇒ "a12de"
fillin the elements of
end. It is an error if
fillis not a character or is forbidden in strings.
#tif the strings are the same length and contain the same characters in the same positions. Otherwise, the
#f.(string=? "Straße" "Strasse") ⇒ #f
These procedures return
#tif their arguments are (respectively): monotonically increasing, monotonically decreasing, monotonically non-decreasing, or monotonically nonincreasing. These predicates are required to be transitive.
These procedures are the lexicographic extensions to strings of the corresponding orderings on characters. For example,
string<?is the lexicographic ordering on strings induced by the ordering
char<?on characters. If two strings differ in length but are the same up to the length of the shorter string, the shorter string is considered to be lexicographically less than the longer string.(string<? "z" "ß") ⇒ #t (string<? "z" "zz") ⇒ #t (string<? "z" "Z") ⇒ #f
These procedures are similar to
string=?, etc., but behave as if they applied
string-foldcaseto their arguments before invoking the corresponding procedures without
-ci.(string-ci<? "z" "Z") ⇒ #f (string-ci=? "z" "Z") ⇒ #t (string-ci=? "Straße" "Strasse") ⇒ #t (string-ci=? "Straße" "STRASSE") ⇒ #t (string-ci=? "ΧΑΟΣ" "χαοσ") ⇒ #t
These procedures take a string argument and return a string result. They are defined in terms of Unicode’s locale–independent case mappings from Unicode scalar–value sequences to scalar–value sequences. In particular, the length of the result string can be different from the length of the input string. When the specified result is equal in the sense of
string=?to the argument, these procedures may return the argument instead of a newly allocated string.
string-upcaseprocedure converts a string to upper case;
string-downcaseconverts a string to lower case. The
string-foldcaseprocedure converts the string to its case–folded counterpart, using the full case–folding mapping, but without the special mappings for Turkic languages. The
string-titlecaseprocedure converts the first cased character of each word, and downcases all other cased characters.(string-upcase "Hi") ⇒ "HI" (string-downcase "Hi") ⇒ "hi" (string-foldcase "Hi") ⇒ "hi" (string-upcase "Straße") ⇒ "STRASSE" (string-downcase "Straße") ⇒ "straße" (string-foldcase "Straße") ⇒ "strasse" (string-downcase "STRASSE") ⇒ "strasse" (string-downcase "Σ") ⇒ "σ" ; Chi Alpha Omicron Sigma: (string-upcase "ΧΑΟΣ") ⇒ "ΧΑΟΣ" (string-downcase "ΧΑΟΣ") ⇒ "χαος" (string-downcase "ΧΑΟΣΣ") ⇒ "χαοσς" (string-downcase "ΧΑΟΣ Σ") ⇒ "χαος σ" (string-foldcase "ΧΑΟΣΣ") ⇒ "χαοσσ" (string-upcase "χαος") ⇒ "ΧΑΟΣ" (string-upcase "χαοσ") ⇒ "ΧΑΟΣ" (string-titlecase "kNock KNoCK") ⇒ "Knock Knock" (string-titlecase "who's there?") ⇒ "Who's There?" (string-titlecase "r6rs") ⇒ "R6rs" (string-titlecase "R6RS") ⇒ "R6rs"
Note: The case mappings needed for implementing these procedures can be extracted from
WordBreakProperty.txt(the “MidLetter” property partly defines case–ignorable characters), and
CaseFolding.txtfrom the Unicode Consortium.
Since these procedures are locale–independent, they may not be appropriate for some locales.
Note: Word breaking, as needed for the correct casing of the upper case greek sigma and for
string-titlecase, is specified in Unicode Standard Annex #29.
Kawa Note: The implementation of
string-titlecasedoes not correctly handle the case where an initial character needs to be converted to multiple characters, such as “LATIN SMALL LIGATURE FL” which should be converted to the two letters
These procedures take a string argument and return a string result, which is the input string normalized to Unicode normalization form D, KD, C, or KC, respectively. When the specified result is equal in the sense of
string=?to the argument, these procedures may return the argument instead of a newly allocated string.(string-normalize-nfd "\xE9;") ⇒ "\x65;\x301;" (string-normalize-nfc "\xE9;") ⇒ "\xE9;" (string-normalize-nfd "\x65;\x301;") ⇒ "\x65;\x301;" (string-normalize-nfc "\x65;\x301;") ⇒ "\xE9;"
Using function-call syntax with strings is convenient and efficient. However, it has some “gotchas”.
We will use the following example string:
(! str1 "Smile \x1f603;!")
or if you’re brave:
(! str1 "Smile 😃!")
"Smile " followed by an emoticon (“smiling face with
open mouth”) followed by
The emoticon has scalar value
\x1f603 - it is not
in the 16-bit Basic Multi-language Plane,
and so it must be encoded by a surrogate pair
#\xd83d followed by
The number of scalar values (
characters) is 8,
while the number of 16-bits code units (
chars) is 9.
length function calls that method;
string-length procedure counts
(length str1) ⇒ 9 (str1:length) ⇒ 9 (string-length str1) ⇒ 8
chars is a constant-time operation (since it
is stored in the data structure), while counting
takes time proportional to the length of the string,
since it has to subtract one for each surrogate pair.
Similarly we can can index the string in 3 ways:
(str1 1) ⇒ #\m :: character (str1:charAt 1) ⇒ #\m :: char (string-ref str1 1) ⇒ #\m :: character
Note using the function-call syntax returns a
Things become interesting when we reach the emoticon:
(str1 6) ⇒ #\😃 :: character (str1:charAt 6) ⇒ #\d83d :: char (string-ref str1 6) ⇒ #\😃 :: character
string-ref and the function-call syntax return the
real character, while the
charAt methods returns a partial character.
string-ref needs to linearly count from the
start of the string, while the function-call syntax can do a constant-time
lookup. It does this by calling
(str1:charAt 6) first.
If that returns a leading-surrogate character, it checks that the
next character (i.e.
(str1:charAt 7)) is a trailing-surrogate character,
and if so combines the two. (If there is no next character or it is not
a trailing-surrogate, then indexing just returns the leading-surrogate
In other words
(string-ref s i) returns the
(s i) return the
If the character at the
i’th index is a surrogate pair,
(s (+ i 1)) returns a special pseudo-character
#\ignorable-char. This pseudo-character should
generally be ignored. (It is automatically ignored by
Kawa functions including
(str1 7) ⇒ #\ignorable-char :: character (str1 8) ⇒ #\! :: character (str1:charAt 7) ⇒ #\de03 :: char (str1:charAt 8) ⇒ #\! :: char (string-ref str1 7) ⇒ #\! :: character (string-ref str1 8) ⇒ throws StringIndexOutOfBoundsException
Following are two possible implementations
of a single-string version of
string-for-each-1 version is simple,
obvious, and traditional - but the execution time is
quadratic in the string length.
string-for-each-2 version requires filtering
#\ignorable-char, but the execution time
is only linear.
(define (string-for-each-1 proc s::string) (! slen (string-length s)) (do ((i ::int 0 (+ i 1))) ((= i slen)) (proc (string-ref s i)))) (define (string-for-each-2 proc s::string) (! slen (length s)) (do ((i ::int 0 (+ i 1))) ((= i slen)) (let ((ch (s i))) (if (not (char=? ch #\ignorable-char)) (proc ch)))))
You can index a string with a list of integer indexes, most commonly a range:
is basically the same as:
This is usually the same as the following:
(The exception is if you select only part of a surrogate pair.)
Generally when working with strings it is best to work with substrings rather than individual characters:
This is equivalent to invoking the
This is much more efficient than the
since the latter has to convert
Indexing into a string (using for example
is inefficient because of the possible presence of surrogate pairs.
Hence given an index
i access normally requires linearly
scanning the string until we have seen
The string-cursor API is defined in terms of abstract “cursor values”, which point to a position in the string. This avoids the linear scan.
Typical usage is:
whatever) (end (string-cursor-end str))) (do ((sc::string-cursor (string-cursor-start str) (string-cursor-next str sc))) ((string-cursor>=? sc end)) (let ((ch (string-cursor-ref str sc))) (
Alternatively, the following may be marginally faster:
whatever) (end (string-cursor-end str))) (do ((sc::string-cursor (string-cursor-start str) (string-cursor-next-quick sc))) ((string-cursor>=? sc end)) (let ((ch (string-cursor-ref str sc))) (if (not (char=? ch #\ignorable-char)) (
The API is non-standard, but is based on that in Chibi Scheme.
An abstract position (index) in a string. Implemented as a primitive
intwhich counts the number of preceding code units (16-bit
Returns a cursor for the start of the string. The result is always 0, cast to a
Returns a cursor for the end of the string - one past the last valid character. Implemented as
(as string-cursor (invoke.
cursor. If the
cursorpoints to the second
charof a surrogate pair, returns
Return the cursor position
count(default 1) character positions forwards beyond
cursor. For each
countthis may add either 1 or 2 (if pointing at a surrogate pair) to the
Increment cursor by one raw
charposition, even if
cursorpoints to the start of a surrogate pair. (In that case the next
#\ignorable-char.) Same as
(+but with the
Return the cursor position
count(default 1) character positions backwards before
Create a substring of the section of
stringbetween the cursors
Is the position of
cursor1respectively before, before or same, same, after, or after or same, as
Performance note: Implemented as the corresponding
Apply the procedure
procto each character position in
stringbetween the cursors