I am trying to truncate a long string of text to a certain length, but want to also make sure that the truncated result ends at a whitespace. I am also going to append an ellipsis afterwards.

For example this:

"This is a very long string that has more characters than I want in it."

becomes this:

"This is a very long string that..."

I am starting with this but obviously this doesn't deal with the problem of ending the string on whitespace.

<%= item.description[0..30] %>&hellip;
有帮助吗?

解决方案

s[0..30].gsub(/\s\w+\s*$/, '...')

The original answer didn't work in the case where the 30 character substring ended on a whitespace character. This solves that.

>> desc="This is some text it is really long"

>> desc[0..30].gsub(/\s\w+$/,'...')
"This is some text it is really "

>> desc[0..30].gsub(/\s\w+\s*$/,'...')
"This is some text it is..."

其他提示

If you're using Rails 4+ you should just use the built-in truncate helper method, e.g.:

<%= truncate item.description, length: 30, separator: /\w+/ %>

The string "…" will be appended to truncated text; to specify a different string, use the :omission option, e.g. omission: "xxx".

For Rails 3.x the :separator option must be a string. Giving :separator => " " will be fine in many cases, but only catches spaces and not other whitespace. One compromise would be to use String#squish, which replaces all sequences of whitespace with a single space (and also trims leading and trailing whitespace), e.g. "foo\n\tbar ".squish yields "foo bar". It would look like this:

<%= truncate item.description.squish, :length => 30, :separator => /\w/,
                                      :omission => "&hellip;" %>

@evfwcqcg's answer is very good. I found it did not work well when

  1. The string contained other characters that are non-space not alphanumerical.
  2. The string is shorter than the desired length.

Demonstration:

>> s = "How about we put some ruby method Class#Method in our string"
=> "How about we put some ruby method Class#Method in our string"
>> s[0..41].gsub(/\s\w+\s*$/, '...')
=> "How about we put some ruby method Class#Me"
>> s[0..999].gsub(/\s\w+\s*$/, '...')
=> "How about we put some ruby method Class#Method in our..."

This is not what I expected.

Here is what I am using to fix this:

def truncate s, length = 30, ellipsis = '...'
  if s.length > length
    s.to_s[0..length].gsub(/[^\w]\w+\s*$/, ellipsis)
  else
    s
  end
end

When doing tests, here is the output:

>> s = "This is some text it is really long"
=> "This is some text it is really long"
>> truncate s
=> "This is some text it is..."

Still behave as expected.

>> s = "How about we put some ruby method Class#Method in our string"
=> "How about we put some ruby method Class#Method in our string"
>> truncate s, 41
=> "How about we put some ruby method Class..."
>> truncate s, 999
=> "How about we put some ruby method Class#Method in our string"

This is more like it.

desc.gsub(/([\w\s]{30}).+/,'\1...')

Expanding on the answer by @evfwcqcg, this is a pure regex that solves the problem of trailing whitespace.

irb(main):031:0> desc="This is some text it is really long"
irb(main):033:0> desc.gsub(/([\w\s]{30}).+/,'\1...')
=> "This is some text it is really..."
irb(main):034:0> desc="This is some text it is really"
=> "This is some text it is really"
irb(main):035:0> desc.gsub(/([\w\s]{30}).+/,'\1...')
=> "This is some text it is really"
irb(main):036:0> desc="This is some text it is real"
=> "This is some text it is real"
irb(main):037:0> desc.gsub(/([\w\s]{30}).+/,'\1...')
=> "This is some text it is real"

I am surprised that none of the answers is really correct (or limited by using rails helper) although this is very old question, so here is the solution.

Lets clearly formulate what it the goal first. We want truncate string s to 30 characters and cut the last word out as well if it can not entirely fit in. We also want to truncate trailing spaces from the result and add ellipsis, if the text was shortened.

If the text is longer then limit, than the shortening is as easy as

s[0,s.rindex(/\s/,30)].rstrip + '...'

If we wanted the entire result to be max 30 characters, than it is as simple as subtracting the length of ellipse from 30. So because we use three dots (and not one three-dot character) than we need

s[0,s.rindex(/\s/,27)].rstrip + '...'

And the final result (with the test whether we need to truncate at all) is:

if s.length<=30
  s
else
  s[0,s.rindex(/\s/,27)].rstrip + '...'
end

Thats it.


Note: There are some shady cases, when the desired result is not obvious. Here they are:

  • If the string ends with lots of spaces (s= "Helo word ") but is shorter than 30. Should the spaces be preserved? - Currently they are.
  • The same as above, but the spaces at the end cross the limit o 30. Like in (s= "Twentyseven chars long text ") - Currently all spaces ad the end are truncated and ellipsis added.
class String
  def trunca(length=100, ellipsis='...')
    self.length > length ? self[0..length].gsub(/\s*\S*\z/, '').rstrip+ellipsis : self.rstrip
  end
end

Example:

-bash> irb
2.0.0p247 :001 > class String
2.0.0p247 :002?>     def trunca(length=100, ellipsis='...')
2.0.0p247 :003?>         self.length > length ? self[0..length].gsub(/\s*\S*\z/, '').rstrip+ellipsis : self.rstrip
2.0.0p247 :004?>       end
2.0.0p247 :005?>   end
 => nil 
2.0.0p247 :006 > s = "This is a very long string that has more characters than I want to display."
 => "This is a very long string that has more characters than I want to display." 
2.0.0p247 :007 > s.trunca(20)
 => "This is a very long..." 
2.0.0p247 :008 > s.trunca(31)
 => "This is a very long string that..." 
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top