Skip to content

Commit abd9072

Browse files
NHDalyStefanKarpinski
authored andcommitted
Add splitpath(p::AbstractString) to Base.Filesystem. (#28156)
1 parent 99e0b3b commit abd9072

File tree

4 files changed

+81
-1
lines changed

4 files changed

+81
-1
lines changed

base/exports.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,7 @@ export
832832
splitdir,
833833
splitdrive,
834834
splitext,
835+
splitpath,
835836

836837
# filesystem operations
837838
cd,

base/path.jl

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ export
1515
relpath,
1616
splitdir,
1717
splitdrive,
18-
splitext
18+
splitext,
19+
splitpath
1920

2021
if Sys.isunix()
2122
const path_separator = "/"
@@ -129,6 +130,12 @@ julia> splitdir("/home/myuser")
129130
"""
130131
function splitdir(path::String)
131132
a, b = splitdrive(path)
133+
_splitdir_nodrive(a,b)
134+
end
135+
136+
# Common splitdir functionality without splitdrive, needed for splitpath.
137+
_splitdir_nodrive(path::String) = _splitdir_nodrive("", path)
138+
function _splitdir_nodrive(a::String, b::String)
132139
m = match(path_dir_splitter,b)
133140
m === nothing && return (a,b)
134141
a = string(a, isempty(m.captures[1]) ? m.captures[2][1] : m.captures[1])
@@ -196,6 +203,41 @@ function pathsep(paths::AbstractString...)
196203
return path_separator
197204
end
198205

206+
"""
207+
splitpath(path::AbstractString) -> Vector{String}
208+
209+
Split a file path into all its path components. This is the opposite of
210+
`joinpath`. Returns an array of substrings, one for each directory or file in
211+
the path, including the root directory if present.
212+
213+
# Examples
214+
```jldoctest
215+
julia> splitpath("/home/myuser/example.jl")
216+
4-element Array{String,1}:
217+
"/"
218+
"home"
219+
"myuser"
220+
"example.jl"
221+
```
222+
"""
223+
function splitpath(p::String)
224+
drive, p = splitdrive(p)
225+
out = String[]
226+
isempty(p) && (pushfirst!(out,p)) # "" means the current directory.
227+
while !isempty(p)
228+
dir, base = _splitdir_nodrive(p)
229+
dir == p && (pushfirst!(out, dir); break) # Reached root node.
230+
if !isempty(base) # Skip trailing '/' in basename
231+
pushfirst!(out, base)
232+
end
233+
p = dir
234+
end
235+
if !isempty(drive) # Tack the drive back on to the first element.
236+
out[1] = drive*out[1] # Note that length(out) is always >= 1.
237+
end
238+
return out
239+
end
240+
199241
joinpath(a::AbstractString) = a
200242

201243
"""

doc/src/base/file.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,5 @@ Base.Filesystem.expanduser
6262
Base.Filesystem.splitdir
6363
Base.Filesystem.splitdrive
6464
Base.Filesystem.splitext
65+
Base.Filesystem.splitpath
6566
```

test/path.jl

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,42 @@
8585
end
8686
@test relpath(S(joinpath("foo","bar")), S("foo")) == "bar"
8787

88+
@testset "splitpath" begin
89+
@test splitpath(joinpath("a","b","c")) == ["a", "b", "c"]
90+
@test splitpath("") == [""]
91+
92+
@test splitpath(joinpath("cats are", "gr8t")) == ["cats are", "gr8t"]
93+
@test splitpath(joinpath(" ", " ")) == [" ", " "]
94+
95+
# Unix-style paths are understood by all systems.
96+
@test splitpath("/a/b") == ["/", "a", "b"]
97+
@test splitpath("/") == ["/"]
98+
@test splitpath("a/") == ["a"]
99+
@test splitpath("a/b/") == ["a", "b"]
100+
@test splitpath("a.dir/b.txt") == ["a.dir", "b.txt"]
101+
@test splitpath("///") == ["/"]
102+
@test splitpath("///a///b///") == ["/", "a", "b"]
103+
104+
if Sys.iswindows()
105+
@test splitpath("C:\\\\a\\b\\c") == ["C:\\", "a", "b", "c"]
106+
@test splitpath("C:\\\\") == ["C:\\"]
107+
@test splitpath("J:\\") == ["J:\\"]
108+
@test splitpath("C:") == ["C:"]
109+
@test splitpath("C:a") == ["C:a"]
110+
@test splitpath("C:a\\b") == ["C:a", "b"]
111+
112+
@test splitpath("a\\") == ["a"]
113+
@test splitpath("a\\\\b\\\\") == ["a","b"]
114+
@test splitpath("a.dir\\b.txt") == ["a.dir", "b.txt"]
115+
@test splitpath("\\a\\b\\") == ["\\", "a","b"]
116+
@test splitpath("\\\\a\\b") == ["\\\\a\\b"] # This is actually a valid drive name in windows.
117+
118+
@test splitpath("/a/b\\c/d\\\\e") == ["/", "a", "b", "c", "d", "e"]
119+
@test splitpath("/\\/\\") == ["/"]
120+
@test splitpath("\\/\\a/\\//b") == ["\\","a","b"]
121+
end
122+
end
123+
88124
@testset "splitting" begin
89125
@test joinpath(splitdir(S(homedir()))...) == homedir()
90126
@test string(splitdrive(S(homedir()))...) == homedir()

0 commit comments

Comments
 (0)