head/tail split for path

T

Trans

I need to split a path by head/*tail.

Ex.

File.head_tail_split('home/foo/bar') #=> [ 'home', 'foo/bar' ]

Sure, I can write a clumsy loop like the following:

def File.head_tail_split(fname)
s = fname
t = []
h = nil
until s == '.'
t << h
s, h = *split(s)
end
return h, File.join(*t.compact)
end

But I'm betting there's a better way. Or maybe there's already an easy
way I'm overlooking?

T.
 
D

David A. Black

Hi --

I need to split a path by head/*tail.

Ex.

File.head_tail_split('home/foo/bar') #=> [ 'home', 'foo/bar' ]

Sure, I can write a clumsy loop like the following:

def File.head_tail_split(fname)
s = fname
t = []
h = nil
until s == '.'
t << h
s, h = *split(s)
end
return h, File.join(*t.compact)
end

But I'm betting there's a better way. Or maybe there's already an easy
way I'm overlooking?

Check out the Pathname feature:

require 'pathname'

p = Pathname.new("a/b/c")

You get p.basename and p.dirname. Both return Pathname objects, but
they're quite string-like and easily converted.


David

--
Rails training from David A. Black and Ruby Power and Light:
Intro to Ruby on Rails July 21-24 Edison, NJ
* Advancing With Rails August 18-21 Edison, NJ
* Co-taught by D.A. Black and Erik Kastner
See http://www.rubypal.com for details and updates!
 
T

Thomas Sawyer

Hi David,
Check out the Pathname feature:

require 'pathname'

p = Pathname.new("a/b/c")

You get p.basename and p.dirname. Both return Pathname objects, but
they're quite string-like and easily converted.

p = Pathname.new("a/b/c")

p.dirname #=> "a/b"
p.basename #=> "c"

But I need "a" and "b/c".

T.
 
D

David A. Black

Hi --

Hi David,


p = Pathname.new("a/b/c")

p.dirname #=> "a/b"
p.basename #=> "c"

But I need "a" and "b/c".

Whoops. Well, you could do:

require 'enumerator'
[path.to_enum:)ascend).to_a[1], path.basename]

or something like:

path.scan(/([^\/]+)\/(.*)/) # with the String path

Pathname#cleanpath might come in handy if you're rolling your own. So
might File::Separator.


David

--
Rails training from David A. Black and Ruby Power and Light:
Intro to Ruby on Rails July 21-24 Edison, NJ
* Advancing With Rails August 18-21 Edison, NJ
* Co-taught by D.A. Black and Erik Kastner
See http://www.rubypal.com for details and updates!
 
B

Bill Walton

Hi Trans,
I need to split a path by head/*tail.

Ex.

File.head_tail_split('home/foo/bar') #=> [ 'home', 'foo/bar' ]

You could do like this.

def head_tail_split(fname)
components = fname.split('/')
[components.shirt, components.join('/')]
end

irb(main):001:0> fname = 'home/foo/bar'
=> "home/foo/bar"
irb(main):002:0> components = fname.split('/')
=> ["home", "foo", "bar"]
irb(main):003:0> [components.shift, components.join('/')]
=> ["home", "foo/bar"]

HTH,
Bill
 
P

Phlip

Bill said:
You could do like this.

def head_tail_split(fname)
components = fname.split('/')
[components.shirt, components.join('/')]
end

irb(main):001:0> fname = 'home/foo/bar'
=> "home/foo/bar"
irb(main):002:0> components = fname.split('/')
=> ["home", "foo", "bar"]
irb(main):003:0> [components.shift, components.join('/')]
=> ["home", "foo/bar"]

That would be safer if fname were a Pathname.

But then, would split only split off the last path part? Or the first?

If the last, how could we roll many splits up, then pop the first?
 
T

Thomas Sawyer

Bill said:
Hi Trans,
I need to split a path by head/*tail.

Ex.

File.head_tail_split('home/foo/bar') #=> [ 'home', 'foo/bar' ]

You could do like this.

def head_tail_split(fname)
components = fname.split('/')
[components.shirt, components.join('/')]
end

irb(main):001:0> fname = 'home/foo/bar'
=> "home/foo/bar"
irb(main):002:0> components = fname.split('/')
=> ["home", "foo", "bar"]
irb(main):003:0> [components.shift, components.join('/')]
=> ["home", "foo/bar"]

That's basically were I ended up too, but using David's File::Separator
suggestion.

However you made me think it would be helpful for Pathname to have:

class Pathname
def to_a
to_s.split(File::Separator) # better definition ?
end
end

The funny thing is that reminds me of a rewrite of Pathname I did a
while back that used an internal array instead of a string to store the
path. It was ~20% faster than the current lib. But alas, no one cared :(

T.
 
A

ara.t.howard

I need to split a path by head/*tail.

Ex.

File.head_tail_split('home/foo/bar') #=> [ 'home', 'foo/bar' ]

Sure, I can write a clumsy loop like the following:

def File.head_tail_split(fname)
s = fname
t = []
h = nil
until s == '.'
t << h
s, h = *split(s)
end
return h, File.join(*t.compact)
end

But I'm betting there's a better way. Or maybe there's already an easy
way I'm overlooking



cfp:~ > cat a.rb
path = File.join 'a', 'b', 'c'

head, tail = path.split( File::SEPARATOR, 2 )

p :head => head, :tail => tail



cfp:~ > ruby a.rb
{:tail=>"b/c", :head=>"a"}




a @ http://codeforpeople.com/
 
M

Martin DeMello

irb(main):002:0> components = fname.split('/')
=> ["home", "foo", "bar"]
irb(main):003:0> [components.shift, components.join('/')]
=> ["home", "foo/bar"]

That's basically were I ended up too, but using David's File::Separator
suggestion.

Don't forget the second argument to split - it'd be handy here.

first, rest = fname.split(File::Separator, 2)

though you'd probably have to thrown in an
if fname.index(File::Separator) == 0 then fname = fname[1..-1]
first

martin
 
P

Phlip

Bill said:
Not sure what you mean here by 'safer'. Say more?

At work we just finished a rewrite of a system that generously manipulated
folders, paths, and files. The old system originally used only strings, and
string surgery, to manipulate paths. (The old system was also very shabby and
patched up; it started as a one-shot script with no structure, etc.)

In the new system we follow a simple rule: If it's a filename, relative path, or
absolute path of any kind, it's a Pathname. This allows us to stay within the
Pathname feature set, and manipulate paths without any string surgery. The
result is much more typesafe.

In theory, Pathnames would be safer if you needed to support \ path delimiters,
and if you needed to support paths with embedded \ or / characters. We are very
good string surgeons, so we never had those problems.
 
D

David A. Black

Hi --

I need to split a path by head/*tail.

Ex.

File.head_tail_split('home/foo/bar') #=> [ 'home', 'foo/bar' ]

Sure, I can write a clumsy loop like the following:

def File.head_tail_split(fname)
s = fname
t = []
h = nil
until s == '.'
t << h
s, h = *split(s)
end
return h, File.join(*t.compact)
end

But I'm betting there's a better way. Or maybe there's already an easy
way I'm overlooking



cfp:~ > cat a.rb
path = File.join 'a', 'b', 'c'

head, tail = path.split( File::SEPARATOR, 2 )

p :head => head, :tail => tail



cfp:~ > ruby a.rb
{:tail=>"b/c", :head=>"a"}

Ironically, I tried that first and discarded it because I had
misunderstood Tom's question and thought he wanted dirname/basename,
and then didn't resurrect it when I finally understood :) The only
tweak I might make would be to use Pathname#clean (or roll one's own)
in case there are consecutive separators.


David
 
A

ara.t.howard

In the new system we follow a simple rule: If it's a filename,
relative path, or absolute path of any kind, it's a Pathname. This
allows us to stay within the Pathname feature set, and manipulate
paths without any string surgery. The result is much more typesafe.

In theory, Pathnames would be safer if you needed to support \ path
delimiters, and if you needed to support paths with embedded \ or /
characters. We are very good string surgeons, so we never had those
problems.



i've had the same experience. this only issue is

cfp:~ > ruby -r pathname -e' Pathname.new("does-not-exist").realpath '
/opt/local/lib/ruby/1.8/pathname.rb:420:in `lstat': No such file or
directory - /Users/ahoward/does-not-exist (Errno::ENOENT)
from /opt/local/lib/ruby/1.8/pathname.rb:420:in `realpath_rec'
from /opt/local/lib/ruby/1.8/pathname.rb:453:in `realpath'
from -e:1


which is to say that certain pathname operations blow up when files do
not exist so code must work with that fact. also, pathnames cannot be
given to many functions that expect strings so you end up with a lot
of 'pathname.to_s' lines in the code. that said, using pathname will
result in better code every time because it deals with the common
errors people make slinging strings as path components.

cheers.

a @ http://codeforpeople.com/
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top