你如何使用拆分和扫描来解析 ruby 中的 URI?
How do you use split and scan to parse a URI in ruby?
假设我在 Ruby
中有这个字符串
str = "/server/ab/file.html
我想得到一个包含
的数组
["/server/", "/ab/", "file.html"]
有没有办法使用拆分或扫描来获取此数组?我尝试了各种组合,但没有完全符合我想要的。我不能使用任何外部库。有任何想法吗?谢谢
如@sawa 所述,问题在于需要您操作字符串的双“/”。
我能想到的最直接的解决办法是:
# removes the '/' at the beginning of the string
# and splits the string to an array
a = str.sub(/^\//, '').split('/') # => ["server", "ab", "file.html"]
# iterates through the array objects EXCEPT the last one,
# (notice three dots '...' instead of two '..'),
# and adds the missing '/'
a[0...-1].each {|s| s << '/'; s.insert(0 , '/')} # => ["/server/", "/ab/"]
a # => ["/server/", "/ab/", "file.html"]
编辑 2
跟进@mudasobwa 的概念、想法和输入,如果您知道第一个字符始终是 '/'
,这将是迄今为止最快的解决方案(参见编辑基准):
a = str[1..-1].split('/')
a << (a.pop.tap { a.map! {|s| "/#{s}/" } } )
祝你好运。
基准测试
看完@mudasobwa 的回答后,我印象非常深刻。我想知道他的解决方案有多快...
...我很惊讶地发现虽然他的解决方案看起来更优雅,但速度要慢得多。
我不知道为什么,但在这种情况下使用 gsub 或 scan 的 Regexp 查找似乎更慢。
这是基准,对于任何感兴趣的人(每秒迭代次数 - 数字越大越好):
require 'benchmark/ips'
str = "/server/ab/file.html"
Benchmark.ips do |b|
b.report("split") do
a = str.sub(/^\//, '').split('/')
a[0...-1].each {|s| s << '/'; s.insert(0 , '/')}
end
b.report("updated split") do
a = str[1..-1].split('/')
a[0...-1].each {|s| s << '/'; s.insert(0 , '/')}
end
b.report("scan") do
str.scan(/(?<=\/)([\w.]+)(\/)?/).map { |(val,slash)| slash ? "/#{val}/" : val }
end
b.report("gsub") do
str.gsub(/(?<=\/)([\w.]+)(\/)?/).map { |m| "#{ && '/'}#{m}" }
end
b.report("mudasobwa's varient") do
a = str[1..-1].split('/')
[*a[0..-2].map { |e| "/#{e}/"}, a[-1]]
end
b.report("mudasobwa's tap concept") do
a = str[1..-1].split('/')
a << (a.pop.tap { a.map! {|s| "/#{s}/" } })
end
end; nil
# results:
#
# Calculating -------------------------------------
# split 39.378k i/100ms
# updated split 45.530k i/100ms
# scan 23.910k i/100ms
# gsub 18.006k i/100ms
# mudasobwa's varient 47.389k i/100ms
# mudasobwa's tap concept
# 51.895k i/100ms
# -------------------------------------------------
# split 517.487k (± 2.9%) i/s - 2.599M
# updated split 653.271k (± 6.4%) i/s - 3.278M
# scan 268.048k (± 6.9%) i/s - 1.339M
# gsub 202.457k (± 3.2%) i/s - 1.026M
# mudasobwa's varient 656.734k (± 4.8%) i/s - 3.317M
# mudasobwa's tap concept
# 761.914k (± 3.2%) i/s - 3.840M
str = str[1..-1].split('/')
=> ["server", "ab", "file.html"]
str[0...-1].map!{|e| "/#{e}/"} << str[-1]
=> ["/server/", "/ab/", "file.html"]
▶ str.gsub(/(?<=\/)([\w.]+)(\/)?/).map { |m| "#{ && '/'}#{m}" }
#⇒ [ "/server/", "/ab/", "file.html" ]
或者,使用 scan
,这更符合语义:
▶ str.scan(/(?<=\/)([\w.]+)(\/)?/).map { |(val,slash)| slash ? "/#{val}/" : val }
可能是最快的解决方案:
▶ a = str[1..-1].split('/')
▶ [*a[0..-2].map { |e| "/#{e}/"}, a[-1]]
#⇒ ["/server/", "/ab/", "file.html"]
完成就地数组更改(嘿,美学家):
▶ a = str[1..-1].split('/')
▶ a.pop.tap do |e|
▷ a.map! do |e|
▷ [-1, 0].each do |i|
▷ e.insert(i, '/')
▷ end
▷ e
▷ end.push e
▷ end
▶ puts a
#⇒ ["/server/", "/ab/", "file.html"]
假设我在 Ruby
中有这个字符串str = "/server/ab/file.html
我想得到一个包含
的数组["/server/", "/ab/", "file.html"]
有没有办法使用拆分或扫描来获取此数组?我尝试了各种组合,但没有完全符合我想要的。我不能使用任何外部库。有任何想法吗?谢谢
如@sawa 所述,问题在于需要您操作字符串的双“/”。
我能想到的最直接的解决办法是:
# removes the '/' at the beginning of the string
# and splits the string to an array
a = str.sub(/^\//, '').split('/') # => ["server", "ab", "file.html"]
# iterates through the array objects EXCEPT the last one,
# (notice three dots '...' instead of two '..'),
# and adds the missing '/'
a[0...-1].each {|s| s << '/'; s.insert(0 , '/')} # => ["/server/", "/ab/"]
a # => ["/server/", "/ab/", "file.html"]
编辑 2
跟进@mudasobwa 的概念、想法和输入,如果您知道第一个字符始终是 '/'
,这将是迄今为止最快的解决方案(参见编辑基准):
a = str[1..-1].split('/')
a << (a.pop.tap { a.map! {|s| "/#{s}/" } } )
祝你好运。
基准测试
看完@mudasobwa 的回答后,我印象非常深刻。我想知道他的解决方案有多快...
...我很惊讶地发现虽然他的解决方案看起来更优雅,但速度要慢得多。
我不知道为什么,但在这种情况下使用 gsub 或 scan 的 Regexp 查找似乎更慢。
这是基准,对于任何感兴趣的人(每秒迭代次数 - 数字越大越好):
require 'benchmark/ips'
str = "/server/ab/file.html"
Benchmark.ips do |b|
b.report("split") do
a = str.sub(/^\//, '').split('/')
a[0...-1].each {|s| s << '/'; s.insert(0 , '/')}
end
b.report("updated split") do
a = str[1..-1].split('/')
a[0...-1].each {|s| s << '/'; s.insert(0 , '/')}
end
b.report("scan") do
str.scan(/(?<=\/)([\w.]+)(\/)?/).map { |(val,slash)| slash ? "/#{val}/" : val }
end
b.report("gsub") do
str.gsub(/(?<=\/)([\w.]+)(\/)?/).map { |m| "#{ && '/'}#{m}" }
end
b.report("mudasobwa's varient") do
a = str[1..-1].split('/')
[*a[0..-2].map { |e| "/#{e}/"}, a[-1]]
end
b.report("mudasobwa's tap concept") do
a = str[1..-1].split('/')
a << (a.pop.tap { a.map! {|s| "/#{s}/" } })
end
end; nil
# results:
#
# Calculating -------------------------------------
# split 39.378k i/100ms
# updated split 45.530k i/100ms
# scan 23.910k i/100ms
# gsub 18.006k i/100ms
# mudasobwa's varient 47.389k i/100ms
# mudasobwa's tap concept
# 51.895k i/100ms
# -------------------------------------------------
# split 517.487k (± 2.9%) i/s - 2.599M
# updated split 653.271k (± 6.4%) i/s - 3.278M
# scan 268.048k (± 6.9%) i/s - 1.339M
# gsub 202.457k (± 3.2%) i/s - 1.026M
# mudasobwa's varient 656.734k (± 4.8%) i/s - 3.317M
# mudasobwa's tap concept
# 761.914k (± 3.2%) i/s - 3.840M
str = str[1..-1].split('/')
=> ["server", "ab", "file.html"]
str[0...-1].map!{|e| "/#{e}/"} << str[-1]
=> ["/server/", "/ab/", "file.html"]
▶ str.gsub(/(?<=\/)([\w.]+)(\/)?/).map { |m| "#{ && '/'}#{m}" }
#⇒ [ "/server/", "/ab/", "file.html" ]
或者,使用 scan
,这更符合语义:
▶ str.scan(/(?<=\/)([\w.]+)(\/)?/).map { |(val,slash)| slash ? "/#{val}/" : val }
可能是最快的解决方案:
▶ a = str[1..-1].split('/')
▶ [*a[0..-2].map { |e| "/#{e}/"}, a[-1]]
#⇒ ["/server/", "/ab/", "file.html"]
完成就地数组更改(嘿,美学家):
▶ a = str[1..-1].split('/')
▶ a.pop.tap do |e|
▷ a.map! do |e|
▷ [-1, 0].each do |i|
▷ e.insert(i, '/')
▷ end
▷ e
▷ end.push e
▷ end
▶ puts a
#⇒ ["/server/", "/ab/", "file.html"]