使用Rails截取字符串?

72

我想按如下方式截断字符串:

输入:

string = "abcd asfsa sadfsaf safsdaf aaaaaaaaaa aaaaaaaaaa dddddddddddddd"

输出:

string = "abcd asfsa sadfsaf safsdaf aa...ddddd"

你是指像只保留前10个字符那样简单地截断它吗? - emboss
9个回答

121

看一下 Rails的String#truncate 或者 String#truncate_words,它们部分实现了你想要的功能。如果你需要测试是否已经截断,可以在截断后的部分之后添加一些最后的内容。

'Once upon a time in a world far far away'.truncate(27)
# => "Once upon a time in a wo..."

# Pass a string to truncate text at a natural break:
'Once upon a time in a world far far away'.truncate(27, separator: ' ')
# => "Once upon a time in a..."

# The last characters will be replaced with the :omission string (defaults to “…”) for a total length not exceeding length:
'And they found that many people were sleeping better.'.truncate(25, omission: '... (continued)')
# => "And they f... (continued)"
# Truncates a given text after a given number of words (words_count):
'Once upon a time in a world far far away'.truncate_words(4)
# => "Once upon a time..."

# Pass a string to specify a different separator of words:
'Once<br>upon<br>a<br>time<br>in<br>a<br>world'.truncate_words(5, separator: '<br>')
# => "Once<br>upon<br>a<br>time<br>in..."

# The last characters will be replaced with the :omission string (defaults to “…”):
'And they found that many people were sleeping better.'.truncate_words(5, omission: '... (continued)')
# => "And they found that many... (continued)"

唉,2.X版本不支持:separator参数。 - JellicleCat
3
无效的方法截断。 - user1735921
3
如果不在视图中,请包含ActionView::Helpers::TextHelper。 - Artem P
你也可以直接在字符串上调用它,例如 "loonggggggggggg text".truncate(17, separator: ' ')。请参见这里 - Ray Jasson

20
你几乎可以不用Rails做到同样的效果:
text.gsub(/^(.{50,}?).*$/m,'\1...')

50 是您所需的长度。


如果您在控制器内部需要将文本转换为字符串,这非常方便。 - Jerome

15
在最简单的情况下:
string = "abcd asfsa sadfsaf safsdaf aaaaaaaaaa aaaaaaaaaa dddddddddddddd"
tr_string = string[0, 20] + "..." + string[-5,5]
或者
def trancate(string, length = 20)
  string.size > length+5 ? [string[0,length],string[-5,5]].join("...") : string
end

# Usage
trancate "abcd asfsa sadfsaf safsdaf aaaaaaaaaa aaaaaaaaaa dddddddddddddd"
#=> "abcd asfsa sadfsaf s...ddddd"
trancate "Hello Beautiful World"
#=> "Hello Beautiful World"
trancate "Hello Beautiful World", 5
#=> "Hello...World"

trancate "Hello", 5 将返回 Hello...,因为字符串长度为5。 - Serabe
难道不应该是 string.size >= length + 8 吗?如果你有一个长度为25个字符的字符串,那么你将得到一个长度为28个字符的字符串。 - Serabe
@Serabe,如果字符串中有26个字符,你会得到28个字符。但是没有人在意。这不是关于长度,而是关于截断。 - fl00r
2
似乎Rails很在意(https://github.com/lifo/docrails/blob/master/activesupport/lib/active_support/core_ext/string/filters.rb#L42) - Serabe

12

自定义省略符的字符截取

与其他一些人在这里提出的建议类似,您可以使用Rails的#truncate方法,并使用自定义省略符作为您字符串的最后部分:

string = "abcd asfsa sadfsaf safsdaf aaaaaaaaaa aaaaaaaaaa dddddddddddddd"

truncate(string, length: 37, omission: "...#{string[-5, 5]}")
# => "abcd asfsa sadfsaf safsdaf aa...ddddd"

正是你想要的。

额外加分

你可能想要将此内容包装在一个名为truncate_middle的自定义方法中,以便为您执行一些花式操作:

# Truncate the given string but show the last five characters at the end.
#
def truncate_middle( string, options = {} )
  options[:omission] = "...#{string[-5, 5]}"    # Use last 5 chars of string.

  truncate( string, options )
end

然后就这样调用它:

string = "abcd asfsa sadfsaf safsdaf aaaaaaaaaa aaaaaaaaaa dddddddddddddd"

truncate_middle( string, length: 37 )
# => "abcd asfsa sadfsaf safsdaf aa...ddddd"

砰!

感谢询问。我认为这是展示长文本片段的有用方式。


10

这可能不是您问题的确切解决方案,但我认为它会帮助您朝正确的方向前进,用一种相当简洁的方式进行操作。

如果您想将“Hello, World!”限制在前五个字母内,可以使用以下方法:

str = "Hello, World!"
str[0...5] # => "Hello"

如果你想要省略号,只需插值即可:

"#{str[0...5]}..." #=> "Hello..."

3
如果str的长度不足6个字符,则省略号仍将添加到结尾 - 可以尝试使用类似于 "#{str[0..5]}#{'...' if str.length > 6}" 的方式来处理。 - chipit24

6
这是String#truncate的源代码。
def truncate(truncate_at, options = {})
  return dup unless length > truncate_at

  options[:omission] ||= '...'
  length_with_room_for_omission = truncate_at - options[:omission].length
  stop = \
    if options[:separator]
      rindex(options[:separator], length_with_room_for_omission) ||      length_with_room_for_omission
    else
      length_with_room_for_omission
    end

   "#{self[0...stop]}#{options[:omission]}"
end

所以,针对你的情况
string.truncate(37, :omission => "...ddddd")

3
那是一个有趣的问题,你可能希望使用JavaScript而不是Ruby来解决它。原因在于,您可能在屏幕上某个地方显示此文本,并且只有一定的宽度可用。因此,与其将链接(或任何文本)缩短到一定数量的字符,您真正想要的是确保您显示的文本从不超过某个宽度。在一定宽度内可以容纳多少个字符取决于您正在使用的字体、间距等(CSS样式)。如果您使用基于Ruby的解决方案,您可以确保一切正常,但如果以后决定更改样式,则可能会全部崩溃。

因此,我建议使用基于JavaScript的解决方案。我以前处理它的方式是使用jQuery截断插件。将插件包含在您的应用程序中。然后,每次页面加载时都可以挂接一些类似以下的JavaScript:

function truncateLongText() {
  $('.my_style1').truncate({
    width: 270,
    addtitle: true
  });
  $('.my_style2').truncate({
    width: 100
  });
}

添加任何需要被截断的其他样式以及它们应该受到的宽度,插件会自动处理其余部分。这样做的额外优点是将整个应用程序的所有截断逻辑放在一个地方,非常方便。


1
我同意,这绝对是一个客户端的问题(在大多数情况下,不像MySQL索引注释)。但是当客户端不是HTML5而是ObjC或Java时会发生什么? - Nate Symer

1

这是一种解决方案,省略了中间部分,只留下结尾的内容:

  def truncate_middle(string, final_length: 20, seperator: '...', trailing_length: 7)
    return string if string.size <= final_length

    effective_trailing_length = trailing_length + seperator.size
    trailing_portion = seperator + string[-trailing_length, trailing_length]
    beginning_length = final_length - effective_trailing_length
    beginning_portion = string[0, beginning_length]

    beginning_portion + trailing_portion
  end

truncate_middle('areallylongstring', final_length: 10, trailing_length: 3)
# => "area...ing"

0

针对仅使用RUBY而非Rails的人。

这里提供了一种解决方案,灵感来自@yegor256在solution中的解决方案。

def truncate(message, options = {})
  length = options[:length] || message.length
  return message unless message.length > length
  options[:omission] ||= '...'
  return message.gsub(/^(.{#{options[:length]},}?).*$/m, '\1'+options[:omission])
end

使用方式如下:

truncate("your very very very long message", :length => 10)

你也可以使用 omission 选项来提供替换文本:

truncate("your very very very long message", :length => 10, :omission => '... more')

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