#reader(lib "docreader.ss" "scribble")
@require["mz.ss"]

@title[#:tag "strings"]{Strings}

A @pidefterm{string} is a fixed-length arary of
@seclink["characters"]{characters}.

@index['("strings" "immutable")]{A} string can be @defterm{mutable} or
@defterm{immutable}. When an immutable string is provided to a
procedure like @scheme[string-set!], the
@exnraise[exn:fail:contract]. String constants generated by the
default reader (see @secref["parse-string"]) are
immutable.

Two strings are @scheme[eq?] when mutating one would mutate the other.
Two strings are @scheme[eqv?] and @scheme[equal?] when they have the
same length and contain the same sequence of characters.

See also: @scheme[immutable], @scheme[symbol->string],
@scheme[bytes->string/utf-8].

@; ----------------------------------------
@section{String Constructors, Selectors, and Mutators}

@defproc[(make-string [k exact-nonnegative-integer?] [char char?
#\nul]) string?]{ Returns a new mutable string of length @scheme[k] where
each position in the string is initialized with the character
@scheme[char].

@examples[(make-string 5 #\z)]}


@defproc[(string [char char?] ...0) string?]{ Returns a new
mutable string whose length is the number of provided @scheme[char]s, and
whose positions are initialized with the given @scheme[char]s.

@examples[(string #\A #\p #\p #\l #\e)]}


@defproc[(string->immutable-string [str string?]) (and/c string?
 immutable?)]{  Returns an immutable string with the same content as
 @scheme[str], returning @scheme[str] itself if @scheme[str] is
 immutable.}


@defproc[(string? [v any/c]) boolean?]{ Returns @scheme[#t] if @scheme[v]
 is a string, @scheme[#f] otherwise.

@examples[(string? "Apple") (string? 'apple)]}


@defproc[(string-length [str string?]) exact-nonnegative-integer?]{
 Returns the length of @scheme[str].

@examples[(string-length "Apple")]}


@defproc[(string-ref [str string?] [k exact-nonnegative-integer?])
 char?]{  Returns the character at position @scheme[k] in @scheme[str].
 The first position in the string cooresponds to @scheme[0], so the
 position @scheme[k] must be less than the length of the string,
 otherwise the @exnraise[exn:fail:contract].

@examples[(string-ref "Apple" 0)]}


@defproc[(string-set! [str (and/c string? (not/c immutable?))] [k
 exact-nonnegative-integer?] [char char?]) void?]{  Changes the
 character position @scheme[k] in @scheme[str] to @scheme[char].  The first
 position in the string cooresponds to @scheme[0], so the position
 @scheme[k] must be less than the length of the string, otherwise the
 @exnraise[exn:fail:contract].

@examples[(define s (string #\A #\p #\p #\l #\e))
          (string-set! s 4 #\y)
          s]}


@defproc[(substring [str string?] [start exact-nonnegative-integer?]
 [end exact-nonnegative-integer? (string-length str)]) string?]{
 Returns a new mutable string that is @scheme[(- @scheme[end]
 @scheme[start])] characters long, and that contains the same characters
 as @scheme[str] from @scheme[start] inclusive to @scheme[end] exclusive.  The
 @scheme[start] and @scheme[end] arguments must be less than the length of
 @scheme[str], and @scheme[end] must be greater than or equal to @scheme[str],
 otherwise the @exnraise[exn:fail:contract].

@examples[(substring "Apple" 1 3)
          (substring "Apple" 1)]}


@defproc[(string-copy [str string?]) string?]{ Returns
 @scheme[(substring str 0)].}


@defproc[(string-copy! [dest (and/c string? (not/c immutable?))]
                       [dest-start exact-nonnegative-integer?]
                       [src string?]
                       [src-start exact-nonnegative-integer? 0] 
                       [src-end exact-nonnegative-integer? (string-length src)])
         void?]{ 
 Changes the characters of @scheme[dest] from positions
 @scheme[dest-start] (inclusive) to @scheme[dest-end] (exclusive) to
 match the characters in @scheme[src] from @scheme[src-start]
 (inclusive). The strings @scheme[dest] and @scheme[src] can be the
 same string, and in that case the destination region can overlap with
 the source region; the destination characters after the copy match
 the source characters from before the copy. If any of
 @scheme[dest-start], @scheme[src-start], or @scheme[src-end]
 are out of range (taking into account the sizes of the strings and
 the source and destination regions), the
 @exnraise[exn:fail:contract].

@examples[(define s (string #\A #\p #\p #\l #\e))
          (string-copy! s 4 "y")
          (string-copy! s 0 s 3 4)
          s]}

@defproc[(string-fill! [dest (and/c string? (not/c immutable?))] [char
 char?]) void?]{ Changes @scheme[dest] so that every position in the
 string is filled with @scheme[char].

@examples[(define s (string #\A #\p #\p #\l #\e))
          (string-fill! s #\q)
          s]}


@defproc[(string-append [str string?] ...0) string?]{  Returns a new
 mutable string that is as long as the sum of the given @scheme[str]s'
 lengths, and that contains the concatenated characters of the given
 @scheme[str]s. If no @scheme[str]s are provided, the result is a zero-length
 string.

@examples[(string-append "Apple" "Banana")]}


@defproc[(string->list [str string?]) (listof char?)]{ Returns a new
 list of characters coresponding to the content of @scheme[str]. That is,
 the length of the list is @scheme[(string-length @scheme[str])], and the
 sequence of characters of @scheme[str] are in the same sequence in the
 result list.

@examples[(string->list "Apple")]}


@defproc[(list->string [lst (listof char?)]) string?]{ Returns a new
 mutable string whose content is the list of characters in @scheme[lst].
 That is, the length of the string is @scheme[(length @scheme[lst])], and
 the sequence of characters in @scheme[lst] is in the same sequence in
 the result string.

@examples[(list->string (list #\A #\p #\p #\l #\e))]}


@; ----------------------------------------
@section{String Comparisons}


@defproc[(string=? [str1 string?] [str2 string?] ...1) boolean?]{ Returns
 @scheme[#t] if all of the arguments are @scheme[eqv?].}

@examples[(string=? "Apple" "apple")
          (string=? "a" "as" "a")]

@define[(string-sort direction folded?) 
         (if folded?
             @elem{Like @scheme[string-ci<?], but checks whether the arguments would be @direction after case-folding.}
             @elem{Like @scheme[string<?], but checks whether the arguments are @|direction|.})]

@defproc[(string<? [str1 string?] [str2 string?] ...1) boolean?]{
 Returns @scheme[#t] if the arguments are lexicographically sorted
 increasing, where individual characters are ordered by
 @scheme[char<?], @scheme[#f] otherwise.

@examples[(string<? "Apple" "apple")
          (string<? "apple" "Apple")
          (string<? "a" "b" "c")]}

@defproc[(string<=? [str1 string?] [str2 string?] ...1) boolean?]{
 @string-sort["nondecreasing" #f]

@examples[(string<=? "Apple" "apple")
          (string<=? "apple" "Apple")
          (string<=? "a" "b" "b")]}

@defproc[(string>? [str1 string?] [str2 string?] ...1) boolean?]{
 @string-sort["decreasing" #f]

@examples[(string>? "Apple" "apple")
          (string>? "apple" "Apple")
          (string>? "c" "b" "a")]}

@defproc[(string>=? [str1 string?] [str2 string?] ...1) boolean?]{
 @string-sort["nonincreasing" #f]

@examples[(string>=? "Apple" "apple")
          (string>=? "apple" "Apple")
          (string>=? "c" "b" "b")]}


@defproc[(string-ci=? [str1 string?] [str2 string?] ...1) boolean?]{
 Returns @scheme[#t] if all of the arguments are @scheme[eqv?] after
 locale-insensitive case-folding via @scheme[string-foldcase].

@examples[(string-ci=? "Apple" "apple")
          (string-ci=? "a" "a" "a")]}

@defproc[(string-ci<? [str1 string?] [str2 string?] ...1) boolean?]{
 Like @scheme[string<?], but checks whether the arguments would be in
 increasing order if each was first case-folded using
 @scheme[string-foldcase] (which is locale-insensitive).

@examples[(string-ci<? "Apple" "apple")
          (string-ci<? "apple" "banana")
          (string-ci<? "a" "b" "c")]}

@defproc[(string-ci<=? [str1 string?] [str2 string?] ...1) boolean?]{
 @string-sort["nondecreasing" #t]

@examples[(string-ci<=? "Apple" "apple")
          (string-ci<=? "apple" "Apple")
          (string-ci<=? "a" "b" "b")]}

@defproc[(string-ci>? [str1 string?] [str2 string?] ...1) boolean?]{
 @string-sort["decreasing" #t]

@examples[(string-ci>? "Apple" "apple")
          (string-ci>? "banana" "Apple")
          (string-ci>? "c" "b" "a")]}

@defproc[(string-ci>=? [str1 string?] [str2 string?] ...1) boolean?]{
 @string-sort["nonincreasing" #t]

@examples[(string-ci>=? "Apple" "apple")
          (string-ci>=? "apple" "Apple")
          (string-ci>=? "c" "b" "b")]}

@; ----------------------------------------
@section{String Conversions}

@defproc[(string-upcase [str string?]) string?]{ Returns a string
 whose characters are the upcase conversion of the characters in
 @scheme[str]. The conversion uses Unicode's locale-independent
 conversion rules that map code-point sequences to code-point
 sequences (instead of simply mapping a 1-to-1 function on code points
 over the string), so the string produced by the conversion can be
 longer than the input string.

@examples[
(string-upcase "abc!")
(string-upcase "Stra\xDFe")
]}

@defproc[(string-downcase [string string?]) string?]{ Like
 @scheme[string-upcase], but the downcase conversion.

@examples[
(string-downcase "aBC!")
(string-downcase "Stra\xDFe")
(string-downcase "\u039A\u0391\u039F\u03A3")
(string-downcase "\u03A3")
]}


@defproc[(string-titlecase [string string?]) string?]{ Like
 @scheme[string-upcase], but the titlecase conversion only for the
 first character in each sequence of cased characters in @scheme[str]
 (ignoring case-ignorable characters).

@examples[
(string-titlecase "aBC  twO")
(string-titlecase "y2k")
(string-titlecase "main stra\xDFe")
(string-titlecase "stra \xDFe")
]}

@defproc[(string-foldcase [string string?]) string?]{ Like
 @scheme[string-upcase], but the case-folding conversion.

@examples[
(string-foldcase "aBC!")
(string-foldcase "Stra\xDFe")
(string-foldcase "\u039A\u0391\u039F\u03A3")
]}

@defproc[(string-normalize-nfd [string string?]) string?]{ Returns a
string that is the Unicode normalized form D of @scheme[string]. If
the given string is already in the corresponding Unicode normal form,
the string may be returned directly as the result (instead of a newly
allocated string).}

@defproc[(string-normalize-nfkd [string string?]) string?]{ Like
 @scheme[string-normalize-nfd], but for normalized form KD.}

@defproc[(string-normalize-nfc [string string?]) string?]{ Like
 @scheme[string-normalize-nfd], but for normalized form C.}

@defproc[(string-normalize-nfkc [string string?]) string?]{ Like
 @scheme[string-normalize-nfd], but for normalized form KC.}

@; ----------------------------------------
@section{Locale-Specific String Operations}

@defproc[(string-locale=? [str1 string?] [str2 string?] ...1)
 boolean?]{  Like @scheme[string=?], but the strings are compared in a
 locale-specific way, based the value of @scheme[current-locale]. See
 @secref["locales"] for more information on locales.}

@defproc[(string-locale<? [str1 string?] [str2 string?] ...1) boolean?]{
 Like @scheme[string<?], but the sort order compares strings in a
 locale-specific way, based the value of @scheme[current-locale]. In
 particular, the sort order may not be simply a lexicographic
 extension of character ordering.}

@defproc[(string-locale>? [str1 string?] [str2 string?] ...1)
 boolean?]{  Like @scheme[string>?], but locale-specific like
 @scheme[string-locale<?].}

@defproc[(string-locale-ci=? [str1 string?] [str2 string?] ...1)
 boolean?]{  Like @scheme[string-locale=?], but strings are compared
 using rules that are both locale-specific and case-insensitive
 (depending on what ``case-insensitive'' means for the current
 locale).}

@defproc[(string-locale-ci<? [str1 string?] [str2 string?] ...1)
 boolean?]{  Like @scheme[string<?], but both locale-sensitive and
 case-insensitive like @scheme[string-locale-ci=?].}

@defproc[(string-locale-ci>? [str1 string?] [str2 string?] ...1)
 boolean?]{  Like @scheme[string>?], but both locale-sensitive and
 case-insensitive like @scheme[string-locale-ci=?].}

@defproc[(string-locale-upcase [string string?]) string?]{ Like
 @scheme[string-upcase], but using locale-specific case-conversion
 rules based the value of @scheme[current-locale].}

@defproc[(string-locale-downcase [string string?]) string?]{ Like
 @scheme[string-downcase], but using locale-specific case-conversion
 rules based the value of @scheme[current-locale].
}