diff --git a/NEWS.md b/NEWS.md index 0a1ee0b1f77aa..11a56e3b90408 100644 --- a/NEWS.md +++ b/NEWS.md @@ -252,6 +252,9 @@ This section lists changes that do not have deprecation warnings. Library improvements -------------------- + * The function `chop` now accepts two arguments `head` and `tail` allowing to specify + number of characters to remove from the head and tail of the string ([#24126]). + * Functions `first` and `last` now accept `nchar` argument for `AbstractString`. If this argument is used they return a string consisting of first/last `nchar` characters from the original string ([#23960]). diff --git a/base/strings/util.jl b/base/strings/util.jl index 8fd145ac4cb0c..0e556aa822ffe 100644 --- a/base/strings/util.jl +++ b/base/strings/util.jl @@ -66,9 +66,12 @@ startswith(a::Vector{UInt8}, b::Vector{UInt8}) = # TODO: fast endswith """ - chop(s::AbstractString) + chop(s::AbstractString, head::Integer=0, tail::Integer=1) -Remove the last character from `s`. +Remove the first `head` and the last `tail` characters from `s`. +The call `chop(s)` removes the last character from `s`. +If it is requested to remove more characters than `length(s)` +then an empty string is returned. # Examples ```jldoctest @@ -77,9 +80,23 @@ julia> a = "March" julia> chop(a) "Marc" + +julia> chop(a, 1, 2) +"ar" + +julia> chop(a, 5, 5) +"" ``` """ -chop(s::AbstractString) = SubString(s, 1, prevind(s, endof(s))) +function chop(s::AbstractString, head::Integer, tail::Integer) + # negative values of head/tail will throw error in nextind/prevind + headidx = head == 0 ? start(s) : nextind(s, start(s), head) + tailidx = tail == 0 ? endof(s) : prevind(s, endof(s), tail) + SubString(s, headidx, tailidx) +end + +# no head/tail version left for performance reasons +chop(s::AbstractString) = SubString(s, start(s), prevind(s, endof(s))) """ chomp(s::AbstractString) diff --git a/test/strings/util.jl b/test/strings/util.jl index 9defbe27ebd44..cf9a4b11ebab4 100644 --- a/test/strings/util.jl +++ b/test/strings/util.jl @@ -231,10 +231,30 @@ end @testset "chomp/chop" begin @test chomp("foo\n") == "foo" @test chomp("fo∀\n") == "fo∀" + @test chomp("foo\r\n") == "foo" + @test chomp("fo∀\r\n") == "fo∀" @test chomp("fo∀") == "fo∀" @test chop("fooε") == "foo" @test chop("foεo") == "foε" @test chop("∃∃∃∃") == "∃∃∃" + @test chop("∀ϵ∃Δ", 0, 0) == "∀ϵ∃Δ" + @test chop("∀ϵ∃Δ", 0, 1) == "∀ϵ∃" + @test chop("∀ϵ∃Δ", 0, 2) == "∀ϵ" + @test chop("∀ϵ∃Δ", 0, 3) == "∀" + @test chop("∀ϵ∃Δ", 0, 4) == "" + @test chop("∀ϵ∃Δ", 0, 5) == "" + @test chop("∀ϵ∃Δ", 1, 0) == "ϵ∃Δ" + @test chop("∀ϵ∃Δ", 2, 0) == "∃Δ" + @test chop("∀ϵ∃Δ", 3, 0) == "Δ" + @test chop("∀ϵ∃Δ", 4, 0) == "" + @test chop("∀ϵ∃Δ", 5, 0) == "" + @test chop("∀ϵ∃Δ", 1, 1) == "ϵ∃" + @test chop("∀ϵ∃Δ", 2, 2) == "" + @test chop("∀ϵ∃Δ", 3, 3) == "" + @test_throws ArgumentError chop("∀ϵ∃Δ", -3, 3) + @test_throws ArgumentError chop("∀ϵ∃Δ", 3, -3) + @test_throws ArgumentError chop("∀ϵ∃Δ", -3, -3) + @test isa(chomp("foo"), SubString) @test isa(chop("foo"), SubString) end