diff --git a/NEWS.md b/NEWS.md index 84adca46a158f..493dc54bef01a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -20,6 +20,8 @@ Build system changes New library functions --------------------- +* `findfirst`, `findlast`, `findnext` and `findprev` now accept a character as first argument + to search for that character in a string passed as the second argument ([#31664]). * New `findall(pattern, string)` method where `pattern` is a string or regex ([#31834]). Standard library changes diff --git a/base/strings/search.jl b/base/strings/search.jl index 8b62b34bc2d07..6e3ff7e2133a0 100644 --- a/base/strings/search.jl +++ b/base/strings/search.jl @@ -104,6 +104,25 @@ julia> findfirst("Julia", "JuliaLang") findfirst(pattern::AbstractString, string::AbstractString) = findnext(pattern, string, firstindex(string)) +""" + findfirst(ch::AbstractChar, string::AbstractString) + +Find the first occurrence of character `ch` in `string`. + +!!! compat "Julia 1.3" + This method requires at least Julia 1.3. + +# Examples +```jldoctest +julia> findfirst('a', "happy") +2 + +julia> findfirst('z', "happy") === nothing +true +``` +""" +findfirst(ch::AbstractChar, string::AbstractString) = findfirst(==(ch), string) + # AbstractString implementation of the generic findnext interface function findnext(testf::Function, s::AbstractString, i::Integer) z = ncodeunits(s) + 1 @@ -255,6 +274,26 @@ julia> findnext("Lang", "JuliaLang", 2) """ findnext(t::AbstractString, s::AbstractString, i::Integer) = _search(s, t, i) +""" + findnext(ch::AbstractChar, string::AbstractString, start::Integer) + +Find the next occurrence of character `ch` in `string` starting at position `start`. + +!!! compat "Julia 1.3" + This method requires at least Julia 1.3. + +# Examples +```jldoctest +julia> findnext(`z`, "Hello to the world", 1) === nothing +true + +julia> findnext(`o`, "Hello to the world", 6) +8 +``` +""" +findnext(ch::AbstractChar, string::AbstractString, ind::Integer) = + findnext(==(ch), string, ind) + """ findlast(pattern::AbstractString, string::AbstractString) @@ -273,6 +312,25 @@ julia> findfirst("Julia", "JuliaLang") findlast(pattern::AbstractString, string::AbstractString) = findprev(pattern, string, lastindex(string)) +""" + findlast(ch::AbstractChar, string::AbstractString) + +Find the last occurrence of character `ch` in `string`. + +!!! compat "Julia 1.3" + This method requires at least Julia 1.3. + +# Examples +```jldoctest +julia> findlast('p', "happy") +4 + +julia> findlast('z', "happy") === nothing +true +``` +""" +findlast(ch::AbstractChar, string::AbstractString) = findlast(==(ch), string) + # AbstractString implementation of the generic findprev interface function findprev(testf::Function, s::AbstractString, i::Integer) if i < 1 @@ -428,6 +486,26 @@ julia> findprev("Julia", "JuliaLang", 6) """ findprev(t::AbstractString, s::AbstractString, i::Integer) = _rsearch(s, t, i) +""" + findprev(ch::AbstractChar, string::AbstractString, start::Integer) + +Find the previous occurrence of character `ch` in `string` starting at position `start`. + +!!! compat "Julia 1.3" + This method requires at least Julia 1.3. + +# Examples +```jldoctest +julia> findprev('z', "Hello to the world", 18) === nothing +true + +julia> findprev('o', "Hello to the world", 18) +15 +``` +""" +findprev(ch::AbstractChar, string::AbstractString, ind::Integer) = + findprev(==(ch), string, ind) + """ occursin(needle::Union{AbstractString,Regex,AbstractChar}, haystack::AbstractString) diff --git a/test/strings/search.jl b/test/strings/search.jl index 61aaea463eae8..8de73e5c652e5 100644 --- a/test/strings/search.jl +++ b/test/strings/search.jl @@ -43,6 +43,28 @@ for str in [astr, GenericString(astr)] @test_throws BoundsError findnext(isequal('a'), str, nextind(str,lastindex(str))+1) end +for str in [astr, GenericString(astr)] + @test_throws BoundsError findnext('z', str, 0) + @test_throws BoundsError findnext('∀', str, 0) + @test findfirst('x', str) == nothing + @test findfirst('\0', str) == nothing + @test findfirst('\u80', str) == nothing + @test findfirst('∀', str) == nothing + @test findfirst('H', str) == 1 + @test findfirst('l', str) == 3 + @test findfirst('e', str) == 2 + @test findfirst('u', str) == nothing + @test findnext('l', str, 4) == 4 + @test findnext('l', str, 5) == 11 + @test findnext('l', str, 12) == nothing + @test findfirst(',', str) == 6 + @test findnext(',', str, 7) == nothing + @test findfirst('\n', str) == 14 + @test findnext('\n', str, 15) == nothing + @test_throws BoundsError findnext('ε', str, nextind(str,lastindex(str))+1) + @test_throws BoundsError findnext('a', str, nextind(str,lastindex(str))+1) +end + # ascii backward search for str in [astr] @test findlast(isequal('x'), str) == nothing @@ -61,6 +83,23 @@ for str in [astr] @test findlast(isequal('\n'), str) == 14 end +for str in [astr] + @test findlast('x', str) == nothing + @test findlast('\0', str) == nothing + @test findlast('\u80', str) == nothing + @test findlast('∀', str) == nothing + @test findlast('H', str) == 1 + @test findprev('H', str, 0) == nothing + @test findlast('l', str) == 11 + @test findprev('l', str, 5) == 4 + @test findprev('l', str, 4) == 4 + @test findprev('l', str, 3) == 3 + @test findprev('l', str, 2) == nothing + @test findlast(',', str) == 6 + @test findprev(',', str, 5) == nothing + @test findlast('\n', str) == 14 +end + # utf-8 forward search for str in (u8str, GenericString(u8str)) @test_throws BoundsError findnext(isequal('z'), str, 0)