1

I am using Ruby on Rails 3.2.9 and Ruby 1.9.3. Given a string I would like to remove all / characters present at the end of that string. That is, for instance:

From "abc/"   to "abc"
From "abc//"  to "abc"
From "abc///" to "abc"
...
From "a/b/c///" to "a/b/c"

How can I make that (maybe by using a regex)?


I tried:

string = string[0,string.length-1] if string.end_with?('/')

but it works just for one character.

4

4 回答 4

5
"abc//".sub %r{/+$}, ''

this will only delete / at the end of the string. I use %r{} to make a regexp because I don't have to escape the / in this case.

于 2012-12-26T16:38:36.687 回答
2

Some benchmarks are in order.

Because Perry Horwich's answer is basically the same as Huluk's, I figured they'd have the same results. Heh, I was wrong. After looking at it and realizing it was doing a string search instead of a regex, I fixed it and added the fixed test.

After that, I did some fiddling to see what I could come up with. My solutions don't seem to be as straightforward as others, but the first does the job equally fast for short strings, maybe a tiny bit faster on the long strings. The second two are a wash as far as when dealing with short string or long, but both are very fast. I reverted to old-school techniques and did them how we'd do it in assembler "back in the day."

require 'benchmark'

n = 1_000_000

def huluk(s)
  s.sub %r{/+$}, ''
end

def apneadiving1(your_string)
  matches = your_string.match(/(.*?)\/*$/)
  matches[1]
end

def apneadiving2(your_string)
  your_string.scan(/(.*?)\/*$/).flatten.first
end

def fixed_perry_horwich(my_beginning_string)
  my_beginning_string.sub(/\/+$/,'')
end

def the_tin_man1(s)
  s.split(%r[/+$]).first
end

def the_tin_man2(s)
  s = s[0..-2] while s[-1] == '/'
  s
end

def the_tin_man3(s)
  s_len = s.length - 1
  s_len -= 1 while s[s_len] == '/'
  s[0..s_len]
end

puts "Ruby #{ RUBY_VERSION }"
puts "#{ n } iterations"

puts

[
  'abc//',
  'abc' * 50 + '//',
  'abc' * 10 + '////',
  '////////////'
].each do |sample_string|

  puts "sample string: #{ sample_string }"
  puts 'huluk:               %s' % huluk(sample_string)
  puts 'perry_horwich:       %s' % perry_horwich(sample_string)
  puts 'fixed_perry_horwich: %s' % fixed_perry_horwich(sample_string)
  puts 'apneadiving1:        %s' % apneadiving1(sample_string)
  puts 'apneadiving2:        %s' % apneadiving2(sample_string)
  puts 'the_tin_man1:        %s' % the_tin_man1(sample_string)
  puts 'the_tin_man2:        %s' % the_tin_man2(sample_string)
  puts 'the_tin_man3:        %s' % the_tin_man3(sample_string)
  puts

  Benchmark.bm(19) do |b|
    2.times do
      b.report('huluk')               { n.times { huluk(sample_string)               } }
      b.report('fixed_perry_horwich') { n.times { fixed_perry_horwich(sample_string) } }
      b.report('apneadiving1')        { n.times { apneadiving1(sample_string)        } }
      b.report('apneadiving2')        { n.times { apneadiving2(sample_string)        } }
      b.report('the_tin_man1')        { n.times { the_tin_man1(sample_string)        } }
      b.report('the_tin_man2')        { n.times { the_tin_man2(sample_string)        } }
      b.report('the_tin_man3')        { n.times { the_tin_man3(sample_string)        } }
      puts
    end
  end

  puts

end

Results:

Ruby 1.9.3
1000000 iterations
sample string: abc//
huluk:               abc
perry_horwich:       abc//
fixed_perry_horwich: abc
apneadiving1:        abc
apneadiving2:        abc
the_tin_man1:        abc
the_tin_man2:        abc
the_tin_man3:        abc

                          user     system      total        real
huluk                 1.840000   0.000000   1.840000 (  1.849929)
fixed_perry_horwich   1.840000   0.010000   1.850000 (  1.847069)
apneadiving1          1.900000   0.010000   1.910000 (  1.900817)
apneadiving2          4.860000   0.010000   4.870000 (  4.883103)
the_tin_man1          1.830000   0.000000   1.830000 (  1.826547)
the_tin_man2          1.150000   0.000000   1.150000 (  1.151420)
the_tin_man3          1.170000   0.010000   1.180000 (  1.168161)

huluk                 1.820000   0.000000   1.820000 (  1.825440)
fixed_perry_horwich   1.830000   0.000000   1.830000 (  1.827306)
apneadiving1          1.850000   0.000000   1.850000 (  1.853751)
apneadiving2          4.830000   0.000000   4.830000 (  4.825394)
the_tin_man1          1.810000   0.000000   1.810000 (  1.811503)
the_tin_man2          1.150000   0.000000   1.150000 (  1.151865)
the_tin_man3          1.170000   0.000000   1.170000 (  1.172259)
sample string: abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc//
huluk:               abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc
perry_horwich:       abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc//
fixed_perry_horwich: abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc
apneadiving1:        abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc
apneadiving2:        abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc
the_tin_man1:        abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc
the_tin_man2:        abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc
the_tin_man3:        abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc

                          user     system      total        real
huluk                 2.120000   0.000000   2.120000 (  2.122156)
fixed_perry_horwich   2.130000   0.010000   2.140000 (  2.136665)
apneadiving1          9.630000   0.030000   9.660000 (  9.656817)
apneadiving2         12.420000   0.030000  12.450000 ( 12.459432)
the_tin_man1          2.080000   0.010000   2.090000 (  2.090112)
the_tin_man2          1.570000   0.010000   1.580000 (  1.573887)
the_tin_man3          1.380000   0.000000   1.380000 (  1.388120)

huluk                 2.130000   0.010000   2.140000 (  2.134414)
fixed_perry_horwich   2.120000   0.010000   2.130000 (  2.129399)
apneadiving1          9.620000   0.020000   9.640000 (  9.646624)
apneadiving2         12.410000   0.020000  12.430000 ( 12.423327)
the_tin_man1          2.080000   0.010000   2.090000 (  2.090938)
the_tin_man2          1.580000   0.010000   1.590000 (  1.581131)
the_tin_man3          1.390000   0.000000   1.390000 (  1.400093)
sample string: abcabcabcabcabcabcabcabcabcabc////
huluk:               abcabcabcabcabcabcabcabcabcabc
perry_horwich:       abcabcabcabcabcabcabcabcabcabc////
fixed_perry_horwich: abcabcabcabcabcabcabcabcabcabc
apneadiving1:        abcabcabcabcabcabcabcabcabcabc
apneadiving2:        abcabcabcabcabcabcabcabcabcabc
the_tin_man1:        abcabcabcabcabcabcabcabcabcabc
the_tin_man2:        abcabcabcabcabcabcabcabcabcabc
the_tin_man3:        abcabcabcabcabcabcabcabcabcabc

                          user     system      total        real
huluk                 1.980000   0.010000   1.990000 (  1.983149)
fixed_perry_horwich   1.970000   0.010000   1.980000 (  1.979796)
apneadiving1          3.500000   0.010000   3.510000 (  3.513885)
apneadiving2          6.320000   0.020000   6.340000 (  6.348327)
the_tin_man1          2.070000   0.010000   2.080000 (  2.075045)
the_tin_man2          2.800000   0.000000   2.800000 (  2.801136)
the_tin_man3          1.840000   0.010000   1.850000 (  1.851506)

huluk                 1.980000   0.010000   1.990000 (  1.980867)
fixed_perry_horwich   1.980000   0.010000   1.990000 (  1.984776)
apneadiving1          3.510000   0.010000   3.520000 (  3.523383)
apneadiving2          6.340000   0.020000   6.360000 (  6.357442)
the_tin_man1          2.070000   0.000000   2.070000 (  2.074559)
the_tin_man2          2.800000   0.010000   2.810000 (  2.814275)
the_tin_man3          1.850000   0.010000   1.860000 (  1.855621)
sample string: ////////////
huluk:               
perry_horwich:       ////////////
fixed_perry_horwich: 
apneadiving1:        
apneadiving2:        
the_tin_man1:        
the_tin_man2:        
the_tin_man3:        

                          user     system      total        real
huluk                 1.980000   0.010000   1.990000 (  1.982700)
fixed_perry_horwich   1.980000   0.010000   1.990000 (  1.984354)
apneadiving1          1.860000   0.000000   1.860000 (  1.869155)
apneadiving2          4.840000   0.020000   4.860000 (  4.852445)
the_tin_man1          2.010000   0.000000   2.010000 (  2.020292)
the_tin_man2          5.060000   0.020000   5.080000 (  5.070202)
the_tin_man3          6.390000   0.020000   6.410000 (  6.412697)

huluk                 1.980000   0.010000   1.990000 (  1.987652)
fixed_perry_horwich   1.980000   0.010000   1.990000 (  1.981360)
apneadiving1          1.860000   0.000000   1.860000 (  1.871577)
apneadiving2          4.850000   0.020000   4.870000 (  4.857329)
the_tin_man1          2.010000   0.010000   2.020000 (  2.023877)
the_tin_man2          5.060000   0.010000   5.070000 (  5.067753)
the_tin_man3          6.410000   0.020000   6.430000 (  6.434523)

I tried to bracket the simple case of "abc//" and something I'd consider a "worser-case" of 152 characters, to antagonize the patterns and algorithms presented.

huluk and fixed_perry_horwich are a wash as far as which is faster, because the difference between them is compiled away as the source is interpreted by Ruby.


EDIT:

A couple changes:

  • I refactored the code to DRY it up.
  • I removed the perry_horwich test from the benchmarks, because it made no sense to test it.
  • I added a test for '////////////' at @pguardiario's recommendation. It shows the result of a worst-case test stripping the delimiters.

The take-away from the test results is to look at the sort of data being tested and select the algorithm that works best for the input. What works well for some inputs doesn't necessarily work as well for others. I found it interesting that the "old-school" methods outran solutions using regex for short trailing-delimiter strings. I think that's a result of Ruby setting up the calls to compiled code. Once the delimiter string got longer, the compiled code would have an advantage.

于 2012-12-26T18:56:00.327 回答
1

Use a regexp to retrieve the expected part of the string:

/(.*?)\/*$/

See on Rubular.

reg = /(.*?)\/*$/
matches = your_string.match(reg)
result = matches[1] 

#or
result = your_string.scan(reg).flatten.first
于 2012-12-26T16:38:54.780 回答
0

This will work for your example:

my_no_slash_terminated_string = my_beginning_string.sub(/\/+$/,'')

Consider if you want to account for white space at or near the end of the string as well.

于 2012-12-26T16:48:32.357 回答