Skip to content

Commit f073830

Browse files
committed
add findlast findprev
1 parent f2904c9 commit f073830

File tree

3 files changed

+74
-15
lines changed

3 files changed

+74
-15
lines changed

base/strings/search.jl

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ findfirst(ch::AbstractChar, string::AbstractString) = findfirst(==(ch), string)
125125

126126
"""
127127
findfirst(pattern::AbstractVector{<:Union{Int8,UInt8}},
128-
A::AbstractVector{<:Union{Int8,UInt8}}) where {T<:Union{Int8,UInt8}}
128+
A::AbstractVector{<:Union{Int8,UInt8}})
129129
130130
Find the first occurrence of sequence `pattern` in vector `A`.
131131
@@ -199,7 +199,7 @@ end
199199

200200
function _searchindex(s::AbstractVector{<:Union{Int8,UInt8}},
201201
t::AbstractVector{<:Union{Int8,UInt8}},
202-
i::Integer) where T <:Union{Int8,UInt8}
202+
i::Integer)
203203
n = length(t)
204204
m = length(s)
205205
f_s = firstindex(s)
@@ -342,7 +342,7 @@ julia> findnext([0x52, 0x62], [0x40, 0x52, 0x62, 0x52, 0x62], 3)
342342
"""
343343
findnext(pattern::AbstractVector{<:Union{Int8,UInt8}},
344344
A::AbstractVector{<:Union{Int8,UInt8}},
345-
start::Integer) where T<:Union{Int8,UInt8} =
345+
start::Integer) =
346346
_search(A, pattern, start)
347347

348348
"""
@@ -363,6 +363,22 @@ julia> findfirst("Julia", "JuliaLang")
363363
findlast(pattern::AbstractString, string::AbstractString) =
364364
findprev(pattern, string, lastindex(string))
365365

366+
"""
367+
findlast(pattern::AbstractVector{<:Union{Int8,UInt8}},
368+
A::AbstractVector{<:Union{Int8,UInt8}})
369+
370+
Find the last occurrence of `pattern` in array `A`. Equivalent to
371+
[`findprev(pattern, A, lastindex(A))`](@ref).
372+
373+
# Examples
374+
```jldoctest
375+
julia> findlast([0x52, 0x62], [0x52, 0x62, 0x52, 0x62])
376+
3:4
377+
```
378+
"""
379+
findlast(pattern::AbstractVector{<:Union{Int8,UInt8}},
380+
A::AbstractVector{<:Union{Int8,UInt8}}) =
381+
findprev(pattern, A, lastindex(A))
366382
"""
367383
findlast(ch::AbstractChar, string::AbstractString)
368384
@@ -436,27 +452,29 @@ function _rsearchindex(s::String, t::String, i::Integer)
436452
end
437453
end
438454

439-
function _rsearchindex(s::ByteArray, t::ByteArray, k::Integer)
440-
n = sizeof(t)
441-
m = sizeof(s)
455+
function _rsearchindex(s::AbstractVector{<:Union{Int8,UInt8}}, t::AbstractVector{<:Union{Int8,UInt8}}, k::Integer)
456+
n = length(t)
457+
m = length(s)
458+
f_s = firstindex(s)
459+
k < f_s && throw(BoundsError(s, k))
442460

443461
if n == 0
444-
return 0 <= k <= m ? max(k, 1) : 0
462+
return 0 <= k <= m ? max(f_s, k) : 0
445463
elseif m == 0
446464
return 0
447465
elseif n == 1
448466
return something(findprev(isequal(_nthbyte(t,1)), s, k), 0)
449467
end
450468

451469
w = m - n
452-
if w < 0 || k <= 0
470+
if w < 0 || k <= f_s
453471
return 0
454472
end
455473

456474
bloom_mask = UInt64(0)
457475
skip = n - 1
458476
tfirst = _nthbyte(t,1)
459-
for j in n:-1:1
477+
for j in reverse(eachindex(t))
460478
bloom_mask |= _search_bloom_mask(_nthbyte(t,j))
461479
if _nthbyte(t,j) == tfirst && j > 1
462480
skip = j - 2
@@ -477,7 +495,7 @@ function _rsearchindex(s::ByteArray, t::ByteArray, k::Integer)
477495

478496
# match found
479497
if j == n
480-
return i
498+
return i + f_s - 1
481499
end
482500

483501
# no match, try to rule out the next character
@@ -497,9 +515,9 @@ function _rsearchindex(s::ByteArray, t::ByteArray, k::Integer)
497515
0
498516
end
499517

500-
function _rsearch(s::Union{AbstractString,ByteArray},
501-
t::Union{AbstractString,AbstractChar,Int8,UInt8},
502-
i::Integer)
518+
function _rsearch(s::Union{AbstractString,AbstractVector{<:Union{Int8,UInt8}}},
519+
t::Union{AbstractString,AbstractChar,AbstractVector{<:Union{Int8,UInt8}}},
520+
i::Integer)
503521
idx = _rsearchindex(s,t,i)
504522
if isempty(t)
505523
idx:idx-1
@@ -552,9 +570,27 @@ julia> findprev('o', "Hello to the world", 18)
552570
15
553571
```
554572
"""
555-
findprev(ch::AbstractChar, string::AbstractString, ind::Integer) =
556-
findprev(==(ch), string, ind)
573+
findprev(ch::AbstractChar, string::AbstractString, start::Integer) =
574+
findprev(==(ch), string, start)
557575

576+
"""
577+
findprev(pattern::AbstractVector{<:Union{Int8,UInt8}}, A::AbstractVector{<:Union{Int8,UInt8}}, start::Integer)
578+
579+
Find the previous occurrence of the sequence `pattern` in vector `A` starting at position `start`.
580+
581+
!!! compat "Julia 1.6"
582+
This method requires at least Julia 1.6.
583+
584+
# Examples
585+
```jldoctest
586+
julia> findprev([0x52, 0x62], [0x40, 0x52, 0x62, 0x52, 0x62], 3)
587+
2:3
588+
```
589+
"""
590+
findprev(pattern::AbstractVector{<:Union{Int8,UInt8}},
591+
A::AbstractVector{<:Union{Int8,UInt8}},
592+
start::Integer) =
593+
_rsearch(A, pattern, start)
558594
"""
559595
occursin(needle::Union{AbstractString,Regex,AbstractChar}, haystack::AbstractString)
560596

test/offsetarray.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,18 @@ end
635635
@test findnext(pattern, OA, 4) === 5:6
636636
@test findnext(pattern, OA, 6) === nothing
637637
@test findnext(pattern, OA, 7) === nothing
638+
@test findnext(pattern, OA, 2) === 3:4
639+
@test findnext(pattern, OA, 4) === 5:6
640+
@test findnext(pattern, OA, 6) === nothing
641+
@test findnext(pattern, OA, 99) === nothing
638642
@test_throws BoundsError findnext(pattern, OA, 1)
643+
644+
@test findlast(pattern, OA) === 5:6
645+
@test findprev(pattern, OA, 2) === nothing
646+
@test findprev(pattern, OA, 4) === 3:4
647+
@test findprev(pattern, OA, 6) === 5:6
648+
@test findprev(pattern, OA, 99) === findlast(pattern, OA)
649+
@test_throws BoundsError findprev(pattern, OA, 1)
639650
end
640651
end
641652
end

test/strings/search.jl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,15 +394,27 @@ end
394394
@testset "UInt8, Int8 vector" begin
395395
for VT in [Int8, UInt8]
396396
A = VT[0x40, 0x52, 0x62, 0x52, 0x62]
397+
397398
@test findfirst(VT[0x30], A) === nothing
398399
@test findfirst(VT[0x52], A) === 2:2
400+
@test findlast(VT[0x30], A) === nothing
401+
@test findlast(VT[0x52], A) === 4:4
402+
399403
pattern = VT[0x52, 0x62]
404+
400405
@test findfirst(pattern, A) === 2:3
401406
@test findnext(pattern, A, 2) === 2:3
402407
@test findnext(pattern, A, 3) === 4:5
403408
@test findnext(pattern, A, 5) === nothing
404409
@test findnext(pattern, A, 99) === nothing
405410
@test_throws BoundsError findnext(pattern, A, -3)
411+
412+
@test findlast(pattern, A) === 4:5
413+
@test findprev(pattern, A, 3) === 2:3
414+
@test findprev(pattern, A, 5) === 4:5
415+
@test findprev(pattern, A, 2) === nothing
416+
@test findprev(pattern, A, 99) === findlast(pattern, A)
417+
@test_throws BoundsError findprev(pattern, A, -2)
406418
end
407419
end
408420

0 commit comments

Comments
 (0)