2009-09-20 10 views
10

Czy ktokolwiek ma jakiś kod pod ręką, że centrum obcina łańcuch w Ruby on Rails?Jak wyciąć łańcuch w środku?

Coś takiego: Np .: "Witaj świecie, jak się masz?" => "Hel ... ty?"

+1

Ile znaków chcesz obciąć? –

+0

Korzystanie z szyn wymaga znajomości pewnych tematów programowania pośredniego i języka Ruby. Dlatego nie rozumiem, jaki jest twój problem - jest naprawdę łatwy do wdrożenia. Niektórzy mogą spierać się o używanie helpera lub łatania małp "String", ale * rozwiązywanie * jest po prostu proste. – samuil

Odpowiedz

9

Oto zmodyfikowana wersja odpowiedź Mike Woodhouse jest. Wymaga dwóch opcjonalnych parametrów: minimalnej długości, aby ciąg był elipsyzowany, a długość krawędzi.

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, zmieniłem moje, a następnie zauważyłem, że zrobiłeś bardziej kompletną wersję. Miły. +1 –

+0

Może powinniśmy przesłać go jako łatkę do zespołu Rails? – khelll

+2

Bardzo skomplikowane. Zobacz moją odpowiedź na znacznie prostsze rozwiązanie z wykorzystaniem istniejących pomocników 'String' Rails. –

5

Ponieważ nie określono liczby znaków, które mają zostać obcięte, przyjmuję (z przykładu), że należy obcinać ciągi, których długość jest większa niż sześć. Następnie możesz użyć czegoś takiego:

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

Wystarczy dostosować zakresy, aby skrócić kolejność znaków.

+0

DigitalRoss ma rację co do "> 9" zamiast "> 6" - jeśli masz ciąg o długości 7, dodajesz dwa znaki _i_ utracone informacje. –

+0

Masz rację, zapomniałeś policzyć rozmiar elipsy. Naprawiono to teraz. –

5

Oto moja propozycja:

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

Naprawdę nie lubię edycji na miejscu, ponieważ nie wiesz, skąd pochodzi ciąg. Co jeśli jest to atrybut modelu i później zadzwonisz zapisać? –

+1

Tak, '(s = s.dup) [3 ...- 4] = ...' może być lepsze, a nawet funkcjonalne w punkcie widzenia "jak czarna skrzynka". Słuszna uwaga. – DigitalRoss

+0

Prawdopodobnie miałeś na myśli (t = s.dup) ... tam –

13

Jak o wersji Regex:

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

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

EDIT: jak sugeruje się w komentarzach, długość parametryzowane (i przy użyciu innej notacji regex tylko dla cholery z nim)

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 

tak ...

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

ale możemy też:

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

Rozwiązuje to problemy zarówno z rozwiązaniami DigitalRoss, jak i JG. +1 –

+0

Bardzo fajny, choć przejąłbym go tylko dla nazwy metody. – Telemachus

+0

Fajnie, choć będzie lepiej, jeśli można go zmodyfikować, aby zaakceptować parametr długości łańcucha, aby zastosować obcięcie tylko wtedy, gdy ciąg jest dłuższy niż ta długość. – khelll

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

dodałem opcję położenia metody Grosser za:

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

Jeśli chcesz pewną stałą długość niezależnie od długości łańcucha, można użyć Rails #truncate:

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

To fantastyczne. Właśnie to szukałem. – Ossie

+1

Zasługujesz na mój upominek. Tego właśnie szukam. – BartSabayton

+0

Jeśli ciąg znaków wynosi na przykład 110, wiele znaków zostanie powtórzonych przed i po elipsie – collimarco

0

Jeśli chcesz ograniczyć długość łańcucha o bajtyzację i potrzebujesz obsługiwać kodowanie Unicode, Wymienione rozwiązania nie będą działać poprawnie.

Oto rozwiązanie, które wykorzystuje rozmiar bajtów i utrzymuje Unicode elementów tekstowych nienaruszony (które nie zawsze są takie same jak znaki Unicode ). Testowane w Ruby 1.8/1.9/2.0.

Można go ulepszyć, aby był bardziej wydajny w przypadku bardzo długich łańcuchów, które muszą zostać przycięte na znacznie mniejszych długościach.

Musisz zainstalować klejnot unicode.

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

Innym sposobem:

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 

Ma to tę dodatkową zaletę, jeśli tylko obcinanie rozmiarów strun < 5.

np. na jeszcze rozmiarze wyrażenie:

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" 

i za ciąg nieparzystej wielkości:

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

Jest to najprostszy sposób dla mnie:

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 

Regards ludzie!