2009-09-20 39 views
10

有没有人有任何代码方便的中心截断Ruby on Rails的字符串?如何中心截断一个字符串?

类似这样的: 例如:“Hello World,你好吗?” =>“你好吗?”

+1

你想要截断多少个字符? – 2009-09-20 16:32:09

+0

使用rails需要您了解一些中级编程主题和Ruby语言。这就是为什么我不明白你的问题是什么 - 这很容易实现。有些人可能会争论使用助手,或猴子补丁'字符串类,但*解*很简单。 – samuil 2009-09-21 13:15:22

回答

9

这里是迈克伍德豪斯的回答修改后的版本。它需要2个可选参数:椭圆化字符串的最小长度和边长。

class String 
    def ellipsisize(minimum_length=4,edge_length=3) 
    return self if self.length < minimum_length or self.length <= edge_length*2 
    edge = '.'*edge_length  
    mid_length = self.length - edge_length*2 
    gsub(/(#{edge}).{#{mid_length},}(#{edge})/, '\1...\2') 
    end 
end 


"abc".ellipsisize #=> "abc" 
"abcdefghi".ellipsisize #=> "abcdefghi" 
"abcdefghij".ellipsisize #=> "abc...hij" 
"abcdefghij".ellipsisize(4,4) #=> "abcd...ghij" 
"Testing all paramas and checking them!".ellipsisize(6,5) #=> "Testi...them!" 
+1

Duh,我改变了我的意思,然后注意到你做了一个更完整的版本。尼斯。 +1 – 2009-09-21 08:19:21

+0

可能我们应该将它作为补丁提交给Rails团队? – khelll 2009-09-22 04:15:53

+2

相当复杂。利用现有的Rails'String'助手,查看我的答案,获得更简单的解决方案。 – 2013-10-13 14:12:17

5

由于您没有指定要截断的字符数,因此我假设(您的示例中)要截断长度大于6的字符串。然后你可以使用像这样的东西:

s = "Hello World, how are you?" 
s = s[0, 3] + "..." + s[-3, 3] if s.length > 9 
=> "Hel...ou?" 

只需调整范围,如果你需要截断更多的字符。

+0

DigitalRoss对“> 9”而不是“> 6”是正确的 - 如果你有一个长度为7的字符串,那么你添加了两个字符和丢失的信息。 – 2009-09-20 19:01:38

+0

你是对的,忘了计算省略号的大小。现在已经修复了。 – 2009-09-20 22:43:58

5

这里是我的建议:

s[3...-4] = "..." if s.length > 9 
+1

我不太喜欢现场编辑,因为您不知道字符串来自哪里。如果它是一个模型属性,并且以后调用save,该怎么办? – 2009-09-20 19:00:34

+1

是的,'(s = s.dup)[3 ...- 4] = ...'可能会更好,甚至在“作为黑箱”的观点中起作用。好点子。 – DigitalRoss 2009-09-20 20:02:27

+0

你可能的意思是(t = s.dup)... there – 2009-09-20 21:02:00

13

怎么样一个正则表达式的版本:

class String 
    def ellipsisize 
    gsub(/(...).{4,}(...)/, '\1...\2') 
    end 
end 

"abc".ellipsisize #=> "abc" 
"abcdefghi".ellipsisize #=> "abcdefghi" 
"abcdefghij".ellipsisize #=> "abc...hij" 

编辑:正如在评论中建议,参数化长度(以及使用不同的正则表达式的符号,只是为它赫克)

class String 
    def ellipsisize(len = 9) 
    len = 9 unless len > 9 # assumes minimum chars at each end = 3 
    gsub(%r{(...).{#{len-5},}(...)}, '\1...\2') 
    end 
end 

所以...

"abcdefghij".ellipsisize #=> "abc...hij" 

但我们也可以:

"abcdefghij".ellipsisize(10) #=> "abcdefghij" 
+0

这解决了DigitalRoss和JG解决方案的问题。 +1 – 2009-09-20 19:08:25

+0

非常好,尽管我只是为了方法的名称而提出了upvoted。 – Telemachus 2009-09-21 00:38:35

+0

很酷,但如果你可以修改它以接受字符串长度参数,只有当字符串长于该长度时才应用截断,它会更好。 – khelll 2009-09-21 02:42:33

3
class String 
    # https://gist.github.com/1168961 
    # remove middle from strings exceeding max length. 
    def ellipsize(options={}) 
    max = options[:max] || 40 
    delimiter = options[:delimiter] || "..." 
    return self if self.size <= max 
    remainder = max - delimiter.size 
    offset = remainder/2 
    (self[0,offset + (remainder.odd? ? 1 : 0)].to_s + delimiter + self[-offset,offset].to_s)[0,max].to_s 
    end unless defined? ellipsize 
end 
0

我添加位置选项,以粗俗的方法:

def ellipsize(str, options={}) 
    max = options[:max] || 40 
    delimiter = options[:delimiter] || "..." 
    position = options[:position] || 0.8 
    return str if str.size <= max 
    remainder = max - delimiter.size 
    offset_left = remainder * position 
    offset_right = remainder * (1 - position) 
    (str[0,offset_left + (remainder.odd? ? 1 : 0)].to_s + delimiter + str[-offset_right,offset_right].to_s)[0,max].to_s 
end 
20

如果你想有一个特定的固定长度不考虑字符串的长度,你可以使用Rails的#truncate

s.truncate(100, omission: "...#{s.last(50)}") 
+0

太棒了。正是我在Google上搜索的内容。 – Ossie 2014-03-10 19:31:10

+1

你应该得到我的赞赏。这是我正在寻找的。 – BartSabayton 2015-06-03 01:25:08

+0

例如,如果字符串是110,省略号前后会重复很多字符。 – collimarco 2016-09-05 18:13:54

0

如果需要限制由bytesize您的字符串长度,你需要处理的unicode则列出的解决方案无法正常工作。

这是一个使用的字节大小,并保持统一文本元素不变(这并不总是相同为Unicode字符)的解决方案。经测试,在红宝石1.8/1.9/2.0。

对于需要截短到更小长度的非常长的字符串,可以对其进行改进以提高效率。

您必须安装unicode gem。

require 'unicode' 

# truncates a unicode string like: 
# >> truncate_string_middle('12345678', 5) 
# => "1...8" 
def truncate_string_middle(str, limit, ellipsis='...') 
    raise "limit (#{limit}) must not be less than the ellipsis size (#{ellipsis.bytesize})" if limit < ellipsis.bytesize 

    return str if str.bytesize <= limit 

    chars = Unicode.text_elements(str) 
    split_point = (chars.size/2.0).ceil 
    front, back = chars[0...split_point], chars[split_point..-1] 

    pop_front = chars.size.odd? 
    # alternate between popping from the front and shifting from the back until it's small enough 
    while (front.join + ellipsis + back.join).bytesize > limit 
    if pop_front 
     front.pop 
    else 
     back.shift 
    end 
    pop_front = !pop_front 
    end 

    front.join + ellipsis + back.join 
end 
1

另一种方式:

class String 
    def middle_truncate(len) 
    return self if len >= size 
    return self[0...len] unless len > 4 
    half = len/2.0 
    (result = dup)[(half - 1.5).floor...(1.5 - half).floor] = '...' 
    result 
    end 
end 

这样做的,如果字符串大小< 5只截断了额外的好处。

例如对于偶数大小的字符串:

2.1.1 :001 > s = "123456789" 
=> "123456789" 
2.1.1 :002 > s.middle_truncate 21 
=> "123456789" 
2.1.1 :003 > s.middle_truncate 20 
=> "123456789" 
2.1.1 :004 > s.middle_truncate 19 
=> "12345678...34567890" 
2.1.1 :005 > s.middle_truncate 18 
=> "1234567...34567890" 
2.1.1 :006 > s.middle_truncate 5 
=> "1...0" 
2.1.1 :007 > s.middle_truncate 4 
=> "1234" 

和奇数大小的字符串:

2.1.1 :012 > s = "123456789" 
=> "123456789" 
2.1.1 :013 > s.middle_truncate 22 
=> "123456789" 
2.1.1 :014 > s.middle_truncate 21 
=> "123456789" 
2.1.1 :015 > s.middle_truncate 20 
=> "12345678...345678901" 
2.1.1 :016 > s.middle_truncate 19 
=> "12345678...45678901" 
2.1.1 :017 > s.middle_truncate 5 
=> "1...1" 
2.1.1 :018 > s.middle_truncate 4 
=> "1234" 
0

这是最简单的方法对我来说:

def ellipsisize(text, minimum_length=12,edge_length=4) 
    leftover = text.length - minimum_length 
    edge_length = leftover if (edge_length > leftover && leftover >= 0) 
    edge_length = 0 if leftover < 0 

    return text.truncate(minimum_length) << text.last(edge_length) 
end 

问候乡亲!