diff --git a/base/strings/util.jl b/base/strings/util.jl index 87c2abab5344c..7ce0b00cfb6b8 100644 --- a/base/strings/util.jl +++ b/base/strings/util.jl @@ -100,6 +100,7 @@ Base.startswith(io::IO, prefix::AbstractString) = startswith(io, String(prefix)) function endswith(a::Union{String, SubString{String}}, b::Union{String, SubString{String}}) + cub = ncodeunits(b) astart = ncodeunits(a) - ncodeunits(b) + 1 if astart < 1 false @@ -1274,3 +1275,33 @@ function Base.rest(s::AbstractString, st...) end return String(take!(io)) end +""" +The `String` constructor is enhanced to accept iterators/generator objects. + +### Method Details: +- **String(x::AbstractIterator)** + - Converts an iterator into a string. + - Throws a `MethodError` if the iterator contains invalid data types (non-Char types) or if it is an infinite iterator. + - Ensures that the result is a valid string representation composed solely of characters (`Char`). + +### Examples +```jldoctest +julia> String(Iterators.map(c -> c+1, "Hello, world")) +"Ifmmp-!xpsme" # Generates a string by incrementing ASCII values of each character. + +julia> String(Iterators.take("Hello, world", 5)) +"Hello" # Takes the first 5 characters of the string and converts it to a string. +""" +String(x) = _string_iterator(x, IteratorSize(x)) +_string_iterator(x, ::IsInfinite) = throw(MethodError(String, (x,))) +_string_iterator(x, ::IteratorSize) = begin + try + collected = collect(Char, x) + if ndims(collected) != 1 + throw(MethodError(String, (x,))) + end + return String(collected) + catch e + throw(MethodError(String, (x,))) + end +end diff --git a/test/strings/util.jl b/test/strings/util.jl index bb87881bbaa1d..4e1da39dd586a 100644 --- a/test/strings/util.jl +++ b/test/strings/util.jl @@ -791,3 +791,40 @@ end @test endswith(A, split(B, ' ')[end]) @test endswith(A, 'g') end +@testset "String Iterator Tests" begin + for S in (String, SubStr, Test.GenericString) + @test String(Iterators.map(c -> c+1, "abc")) == "bcd" + @test String(Iterators.take("hello world", 5)) == "hello" + @test String(Iterators.filter(c -> c != ' ', "hello world")) == "helloworld" + @test String(Iterators.drop("hello world", 6)) == "world" + @test String(Iterators.map(c -> 'a', "hello")) == "aaaaa" + @test String(Iterators.map(c -> c == ' ' ? ' ' : 'A', "hello world")) == "AAAAA AAAAA" + @test String(Iterators.map(c -> 'šŸ˜Š', "hello")) == "šŸ˜ŠšŸ˜ŠšŸ˜ŠšŸ˜ŠšŸ˜Š" + @test String(Iterators.take("", 3)) == "" + @test String(Iterators.drop("", 3)) == "" + @test_throws MethodError String(Iterators.filter(c -> c > 128, "hello")) + @test_throws MethodError String(Iterators.cycle("abc")) + @test_throws MethodError String(19) + @test_throws MethodError String(3.14) + @test String(Iterators.map(c -> Char(c), "abc")) == "abc" + @test String(Iterators.flatten(Iterators.map(c -> c, ["hello", "world"]))) == "helloworld" + @test String(Iterators.flatten(Iterators.map(c -> "hello", 1:3))) == "hellohellohello" + @test String(Iterators.filter(c -> false, "hello")) == "" + @test String(Iterators.map(c -> ' ', "hello world")) == " " + @test String(Iterators.map(c -> 'šŸ˜Š', "hi there")) == "šŸ˜ŠšŸ˜ŠšŸ˜ŠšŸ˜ŠšŸ˜ŠšŸ˜ŠšŸ˜ŠšŸ˜Š" + @test String("ƄƖƜā»Ø") == "ƄƖƜā»Ø" + @test String(Iterators.map(c -> 'A', "ƄƖƜā»Ø")) == "AAAA" + @test String(Iterators.map(c -> c == 'Ɩ' ? 'O' : c, "ƄƖƜā»Ø")) == "ƄOƜā»Ø" + @test String(Iterators.take("ƄƖƜā»Ø", 2)) == "ƄƖ" + @test String(Iterators.drop("ƄƖƜā»Ø", 1)) == "ƖƜā»Ø" + end + @test_throws MethodError String(Iterators.cycle("abc")) + @test String("valid string") == "valid string" + @test String("test") == "test" + @test String(Iterators.flatten(Iterators.map(c -> "abc", 1:3))) == "abcabcabc" + @test String(Iterators.flatten(Iterators.map(c -> "šŸ˜Š", 1:2))) == "šŸ˜ŠšŸ˜Š" + @test String(Iterators.filter(c -> false, "hello")) == "" + @test String(Iterators.take("hello", 0)) == "" + @test String(Iterators.take("a"^1000, 500)) == "a"^500 + @test String(Iterators.map(c -> 'a', "a"^100000)) == "a"^100000 +end