@@ -700,12 +700,11 @@ _free_pat_replacer(x) = nothing
700700_pat_replacer (x:: AbstractChar ) = isequal (x)
701701_pat_replacer (x:: Union{Tuple{Vararg{AbstractChar}},AbstractVector{<:AbstractChar},Set{<:AbstractChar}} ) = in (x)
702702
703- function replace (str :: String , pat_repl :: Vararg{Pair,N} ; count :: Integer = typemax (Int)) where N
704- count == 0 && return str
703+ # note: leave str untyped here to make it easier for packages like StringViews to hook in
704+ function _replace_init (str, pat_repl :: NTuple{N, Pair} , count:: Int ) where N
705705 count < 0 && throw (DomainError (count, " `count` must be non-negative." ))
706- n = 1
707- e1 = nextind (str, lastindex (str)) # sizeof(str)
708- i = a = firstindex (str)
706+ e1 = nextind (str, lastindex (str)) # sizeof(str)+1
707+ a = firstindex (str)
709708 patterns = map (p -> _pat_replacer (first (p)), pat_repl)
710709 replaces = map (last, pat_repl)
711710 rs = map (patterns) do p
@@ -716,21 +715,24 @@ function replace(str::String, pat_repl::Vararg{Pair,N}; count::Integer=typemax(I
716715 r isa Int && (r = r: r) # findnext / performance fix
717716 return r
718717 end
719- if all (> (e1), map (first, rs))
720- foreach (_free_pat_replacer, patterns)
721- return str
722- end
723- out = IOBuffer (sizehint= floor (Int, 1.2 sizeof (str)))
718+ return e1, patterns, replaces, rs, all (> (e1), map (first, rs))
719+ end
720+
721+ # note: leave str untyped here to make it easier for packages like StringViews to hook in
722+ function _replace_finish (io:: IO , str, count:: Int ,
723+ e1:: Int , patterns:: Tuple , replaces:: Tuple , rs:: Tuple )
724+ n = 1
725+ i = a = firstindex (str)
724726 while true
725727 p = argmin (map (first, rs)) # TODO : or argmin(rs), to pick the shortest first match ?
726728 r = rs[p]
727729 j, k = first (r), last (r)
728730 j > e1 && break
729731 if i == a || i <= k
730732 # copy out preserved portion
731- GC. @preserve str unsafe_write (out , pointer (str, i), UInt (j- i))
733+ GC. @preserve str unsafe_write (io , pointer (str, i), UInt (j- i))
732734 # copy out replacement string
733- _replace (out , replaces[p], str, r, patterns[p])
735+ _replace (io , replaces[p], str, r, patterns[p])
734736 end
735737 if k < j
736738 i = j
@@ -755,13 +757,39 @@ function replace(str::String, pat_repl::Vararg{Pair,N}; count::Integer=typemax(I
755757 n += 1
756758 end
757759 foreach (_free_pat_replacer, patterns)
758- write (out, SubString (str, i))
759- return String (take! (out))
760+ write (io, SubString (str, i))
761+ return io
762+ end
763+
764+ # note: leave str untyped here to make it easier for packages like StringViews to hook in
765+ function _replace_ (io:: IO , str, pat_repl:: NTuple{N, Pair} , count:: Int ) where N
766+ if count == 0
767+ write (io, str)
768+ return io
769+ end
770+ e1, patterns, replaces, rs, notfound = _replace_init (str, pat_repl, count)
771+ if notfound
772+ foreach (_free_pat_replacer, patterns)
773+ write (io, str)
774+ return io
775+ end
776+ return _replace_finish (io, str, count, e1, patterns, replaces, rs)
760777end
761778
779+ # note: leave str untyped here to make it easier for packages like StringViews to hook in
780+ function _replace_ (str, pat_repl:: NTuple{N, Pair} , count:: Int ) where N
781+ count == 0 && return str
782+ e1, patterns, replaces, rs, notfound = _replace_init (str, pat_repl, count)
783+ if notfound
784+ foreach (_free_pat_replacer, patterns)
785+ return str
786+ end
787+ out = IOBuffer (sizehint= floor (Int, 1.2 sizeof (str)))
788+ return String (take! (_replace_finish (out, str, count, e1, patterns, replaces, rs)))
789+ end
762790
763791"""
764- replace(s::AbstractString, pat=>r, [pat2=>r2, ...]; [count::Integer])
792+ replace([io::IO], s::AbstractString, pat=>r, [pat2=>r2, ...]; [count::Integer])
765793
766794Search for the given pattern `pat` in `s`, and replace each occurrence with `r`.
767795If `count` is provided, replace at most `count` occurrences.
@@ -774,13 +802,21 @@ If `pat` is a regular expression and `r` is a [`SubstitutionString`](@ref), then
774802references in `r` are replaced with the corresponding matched text.
775803To remove instances of `pat` from `string`, set `r` to the empty `String` (`""`).
776804
805+ The return value is a new string after the replacements. If the `io::IO` argument
806+ is supplied, the transformed string is instead written to `io` (returning `io`).
807+ (For example, this can be used in conjunction with an [`IOBuffer`](@ref) to re-use
808+ a pre-allocated buffer array in-place.)
809+
777810Multiple patterns can be specified, and they will be applied left-to-right
778811simultaneously, so only one pattern will be applied to any character, and the
779812patterns will only be applied to the input text, not the replacements.
780813
781814!!! compat "Julia 1.7"
782815 Support for multiple patterns requires version 1.7.
783816
817+ !!! compat "Julia 1.10"
818+ The `io::IO` argument requires version 1.10.
819+
784820# Examples
785821```jldoctest
786822julia> replace("Python is a programming language.", "Python" => "Julia")
@@ -799,8 +835,12 @@ julia> replace("abcabc", "a" => "b", "b" => "c", r".+" => "a")
799835"bca"
800836```
801837"""
838+ replace (io:: IO , s:: AbstractString , pat_f:: Pair... ; count= typemax (Int)) =
839+ _replace_ (io, String (s), pat_f, Int (count))
840+
802841replace (s:: AbstractString , pat_f:: Pair... ; count= typemax (Int)) =
803- replace (String (s), pat_f... , count= count)
842+ _replace_ (String (s), pat_f, Int (count))
843+
804844
805845# TODO : allow transform as the first argument to replace?
806846
0 commit comments