Ruby中如何从字符串中删除最后N个字符?

296

如何最好地从字符串中移除最后的n个字符?


如果您知道后缀(顶部答案建议很多人来这里寻找此内容),则可以使用 Ruby 2.5 中的 delete_suffix更多信息请参见此处 - SRack
13个回答

353
irb> 'now is the time'[0...-4]
=> "now is the "

9
注意,这仅适用于Ruby 1.9及以上版本。在Ruby 1.8中,这将删除最后一个字节,而不是最后一个字符 - Jörg W Mittag
2
但如果他使用的是1.8.x版本,他可能指的是“字节”。 - DigitalRoss
4
可以确认 'abc123'.chomp('123') 在短字符串和长字符串情况下都比这个方法快两倍。 (Ruby 2.0.0-p247) - Plasmarob
14
对于那些想知道为什么这个特定示例不起作用的人,原因是有3个点而不是2个,即[0...-4]而不是[0..-4] - rorofromfrance
2
@Plamarob,我认为更相关的是说chomp比说它快两倍要快53纳秒。这样你可以更好地估计使用它的成本与价值,以及在特定情况下是否需要进行优化。 - cesoid
显示剩余4条评论

274
如果您想要删除的字符总是相同的,那么请考虑使用`chomp`函数:
'abc123'.chomp('123')    # => "abc"
< p > chomp 的优点是:不需要计数,代码更清晰地传达了它正在做的事情。

如果没有参数,chomp 会删除 DOS 或 Unix 行尾结束符(如果存在):

"abc\n".chomp      # => "abc"
"abc\r\n".chomp    # => "abc"

从评论中,有人提出使用#chomp和使用范围的速度问题。这里是一个比较这两种方法的基准测试:

require 'benchmark'

S = 'asdfghjkl'
SL = S.length
T = 10_000
A = 1_000.times.map { |n| "#{n}#{S}" }

GC.disable

Benchmark.bmbm do |x|
  x.report('chomp') { T.times { A.each { |s| s.chomp(S) } } }
  x.report('range') { T.times { A.each { |s| s[0...-SL] } } }
end

基准测试结果(使用 CRuby 2.13p242):

Rehearsal -----------------------------------------
chomp   1.540000   0.040000   1.580000 (  1.587908)
range   1.810000   0.200000   2.010000 (  2.011846)
-------------------------------- total: 3.590000sec

            user     system      total        real
chomp   1.550000   0.070000   1.620000 (  1.610362)
range   1.970000   0.170000   2.140000 (  2.146682)

使用chomp比使用范围快,速度提高了约22%。


5
切割也是一种选择。它对需要被去除的部分要求不那么严格。 - Andrew Grimm
1
我发现相反的情况实际上是正确的。 事实是,.chomp 看起来始终快两倍。 - Plasmarob
3
@Plamarob,在Ruby中进行基准测试往往会出现令人惊讶的结果。我常常猜错,所以不再试图去猜哪个更快。另外,如果基准测试结果能够改进回答,请随意编辑它。 - Wayne Conrad
1
如果我正确地阅读了代码,那么range比chomp慢大约53纳秒。在某些情况下,这可能是一个显著的负担,但是牢记绝对速度(而不仅仅是相对速度)可能会告诉您是否应该浏览您的代码库并将所有范围更改为chomps(适用的情况下)。可能不需要这样做。 - cesoid
1
运行良好。您可以使用 chomp!() 原地对字符串进行操作。 - Joshua Pinter
显示剩余2条评论

80

Ruby 2.5+

从 Ruby 2.5 开始,您可以使用 delete_suffixdelete_suffix! 方法以快速且易读的方式实现此操作。

有关这些方法的文档在此处

如果您知道后缀是什么,那么这种方法是惯用的(我认为,甚至比其他答案更易读):

'abc123'.delete_suffix('123')     # => "abc"
'abc123'.delete_suffix!('123')    # => "abc"

使用 bang 方法甚至比最优解快了近40%。以下是相同基准测试的结果:

                     user     system      total        real
chomp            0.949823   0.001025   0.950848 (  0.951941)
range            1.874237   0.001472   1.875709 (  1.876820)
delete_suffix    0.721699   0.000945   0.722644 (  0.723410)
delete_suffix!   0.650042   0.000714   0.650756 (  0.651332)

我希望这对你有用 - 请注意,该方法当前不接受正则表达式,因此如果你不知道后缀,目前无法使用。然而,由于被接受的答案(更新:撰写时)也是这样规定的,我认为这可能对一些人有用。


2
@JPSilvashy 我从来没有因为失去勾号而感到如此高兴。这是一个很棒的答案。 - Wayne Conrad
4
这是一个很棒和快速的函数,但它不是正确答案,因为它没有回答问题,也就是“从字符串中删除最后n个字符的首选方法是什么?” - Sam
1
感谢@Sam的反馈-这个问题已经有了一个近九年的答案,与此相同,目前有261个赞。JP接受了这个答案,并最近切换到了这个答案,所以我认为它回答了他们的问题:)我想把这个现代化的替代方案放在这里,希望能帮助到来到这里的人们。事实上,我已经在问题中涵盖了所有这些内容-我希望这种方法很快就能接受正则表达式,虽然在这个下面有一个完美的替代方案适用于你的情况。 - SRack

64
str = str[0..-1-n]

[0...-n] 不同,这个处理了 n=0 的情况。


5
请注意,这仅适用于Ruby 1.9。在Ruby 1.8中,它将删除最后的字节而不是最后的字符 - Jörg W Mittag
5
这比被选择的答案更好,因为三个点(...)更容易记忆,因为“-n”意味着从字符串结尾处删除n个字符。 - lulalala
同时,"abcd"[0..-2] #=> "abc" 而 "abcd"[0...-2] #=> "ab"。在我看来,使用三个点的范围选项可以使代码更加自解释。 - mokagio
1
请注意,这对于n == 0无效。 str [0 ...-0]""而不是"123"。 您实际上想要str [0..-(n + 1)] - movermeyer
@movermeyer,你的编辑被JS和Bash的粉丝拒绝了。很抱歉,SO是个****网站,90%的人只是来这里拒绝编辑、关闭和删除问题和答案。你可以阅读我的个人资料了解更多信息,并理解为什么我不再是一个活跃的贡献者。你在我11年前的回答中发现了一个真正的错误,干得好。我已经重新接受了这个编辑并稍作修改。 - Nakilon

32

我建议使用 chop。我认为在其中的一篇评论中提到过,但没有链接或解释,下面是我觉得它更好的原因:

它只是从字符串中删除最后一个字符,您不需要指定任何值就可以完成操作。

如果您需要删除多于一个字符,则最好使用chomp。这是Ruby文档chop的描述:

返回删除了最后一个字符的新字符串。如果字符串以 \r\n 结尾,则两个字符都会被删除。将 chop 应用于空字符串将返回空字符串。通常情况下,String#chomp 是更安全的选择,因为如果字符串不以记录分隔符结尾,则它不会更改该字符串。

虽然这主要用于删除分隔符(例如 \r\n),但我已经将其用于从简单字符串中删除最后一个字符,例如将s去掉以使单词变为单数。


1
那不会只是删除最后一个字符吗?问题是关于“字符”(复数形式)的。 - maetthew
1
是的,你说得对,但 chomp('chars') 将删除最后一个 'chars'。我不确定 OP 是想要特定的字符还是只是 N 个字符。 - kakubei
是的,这就是答案,只有在你想要删除一个字符时才使用(虽然这是我的情况)。 - Quv

30
name = "my text"
x.times do name.chop! end

在控制台中:

>name = "Nabucodonosor"
 => "Nabucodonosor" 
> 7.times do name.chop! end
 => 7 
> name
 => "Nabuco" 

这个效率高吗?似乎值得与其他答案进行基准比较 :) - SRack

18

去掉末尾的 n 个字符与保留前 length - n 个字符是相同的。

Active Support 包括 String#firstString#last 方法,提供了一种方便的方法来保留或丢弃前/后面的 n 个字符:

require 'active_support/core_ext/string/access'

"foobarbaz".first(3)  # => "foo"
"foobarbaz".first(-3) # => "foobar"
"foobarbaz".last(3)   # => "baz"
"foobarbaz".last(-3)  # => "barbaz"

3
Rails 6.0 在 "foobarbaz".first(-3) 上发出了一个过时警告:"在 Rails 6.1 中,使用负整数作为 String#first 的参数将会抛出 ArgumentError 异常。" - Toby 1 Kenobi

7
如果你正在使用Rails,尝试使用以下方法:
"my_string".last(2) # => "ng"

要获取不包含最后两个字符的字符串:
n = "my_string".size
"my_string"[0..n-3] # => "my_stri"

注意: 最后一个字符串字符位于n-1。因此,要删除最后2个字符,我们使用n-3。

2
这不会删除最后两个字母,而是返回它们。 - JP Silvashy
1
你不需要先测量字符串,Ruby本身使用从字符串末尾开始的负索引。 - Devon Parsons

5

2
请注意,这仅适用于Ruby 1.9。在Ruby 1.8中,这将删除最后一个字节,而不是最后一个字符 - Jörg W Mittag

3
你可以随时使用类似于的东西
 "string".sub!(/.{X}$/,'')

在这里,X代表要删除的字符数。

或者使用赋值/使用结果的方法:

myvar = "string"[0..-X]

其中X是要移除的字符数加一


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接