简体   繁体   中英

How can I split a long string in two at the nth space?

These strings may be long paragraphs, so I'm not sure it's best to split the entire string with a space delimiter. I'm trying to get, say, the first 10 words and wrap them in a span:

'<span class="easing">' + string + '</span>'

Then rejoin that with the second half of the original split. Suggestions on a super efficient way to do this? It would affect at most three paragraphs on the page at a time.

EDITED

Here's a kicker — The split should occur after the 9th word OR at the end of the first sentence (if that sentence is less than 9 words).

EXAMPLE

var origString = 'Coming into the world on Elvis’ birthday with a doctor named Presley seemed fortuitous until, wielding the silvery smooth scalpel in his aged unsteady hand, the doctor sliced through the walls of my mother’s uterus and into my unborn skin. Inside the warm soothing waters of my mother’s womb, inside the silent weightlessness, I was safe. Then the prick of cold steel marked the first in a series of rude awakenings. I was scarred for life even before birth.';
var newString = '<span="easing">Coming into the world on Elvis’ birthday with a doctor</span> named Presley seemed fortuitous until, wielding the silvery smooth scalpel in his aged unsteady hand, the doctor sliced through the walls of my mother’s uterus and into my unborn skin. Inside the warm soothing waters of my mother’s womb, inside the silent weightlessness, I was safe. Then the prick of cold steel marked the first in a series of rude awakenings. I was scarred for life even before birth.';

Or with a short sentence that starts the paragraph:

var origString = '“Is he okay? Tell me everything’s okay” she pleas, her desperate need to confirm my health competing with her own need for consolation.';
var newString = '<span class="easing">“Is he okay?</span> Tell me everything’s okay” she pleas, her desperate need to confirm my health competing with her own need for consolation.';

Considering you are only going to be scanning at most about 100 chars (unless you have URIs or very long words) then scanning character by character is quite optimal. You could optimise this by using .indexOf() in certain places, but you'd loose what you gained in having to check for each different character that could terminate a sentence.

function spanomatic ( str, words ) {
  var i, l, c;
  for ( i=0, l=str.length; i<l; i++ ) {
    c = str.charAt(i);
    if ( c == ' ' ) {
      if ( words-- <= 0 ) {
        str = '<span>'+str.substring(0,i)+'</span>'+str.substring(i);
        break;
      }
    }
    else if ( ('?!.;:').indexOf(c) != -1 ) {
      str = '<span>'+str.substring(0,i)+'</span>'+str.substring(i);
      break;
    }
  }
  return str;
}

spanomatic ( 'Pass your string here', 9 );

(The above code assumes your text will always be correctly gramatically termintated (ie contain at least one of ?!.;:) - if not then it would be possible for a paragraph with less than 9 words to end up spanless. This could be fixed by a few changes however...)

note for future readers

If you're going for a 'super efficient' way of doing string searching avoid Regular Expressions (unless you really need their power) . The accepted answer for this question is concise and nicely put together function - don't get me wrong - but it's about 70% slower than just scanning the string with a for loop (in my tests on FireFox & Chrome at least) ... and that's even when comparing after moving the Regular Expression definitions outside of Bergi's function (ie using pre-compiled regexps rather than recreating them every time the function is called) .

http://jsperf.com/compare-regexp-vs-char-scanning

return string.replace(/.+?[,.?!]|.+$/, function(match, index, string){
    var words = match.split(/\s+/);
    words[ words.length<10 ? words.length-1 : 9 ] += '</span>';
    return '<span class="easing">' + words.join(" ");
});

This matches the first sentence-like thing (or the whole string - unless linebreaks), and wraps the first 10 words of it in that span. Works for both your sample inputs, but also on smaller ones. Returns the empty string for an empty string, change the regex to …|.*$ if you want an empty span.

How about this code:

var str = 'asda adsfadsf asdfadfadsf adsfsdafadf. adfadfadfad adfadfdaf adfadfadf adfadf \afsgasfggasfg SFGDFGDSFGH dfghdsghdgas hadghdagh';

var sentences = [], words = str.split(' ');
for (var i = 0; i < 9; i++) {
    if (words[i].lastIndexOf('.') !== -1) {
        sentences.push(words[i]);
        break;    
    } else {
        sentences.push(words[i]);
    }        
}

words.slice(sentences.length, words.length);


$('<span>' + sentences.join(' ') + '</span>').appendTo($('#log'));

I have it under fiddle so you can test. You would want to do this in a loop with the remainder of arr1.

Update:

If it's not just the full stop but also ?!:;etc. then create a RegExp and test instead of doing lastIndexOf('.')

Here. It's a bit code-golfy though. Sorry.

$( 'p' ).html(function ( i, text ) {
    var re = /(.+?)\s/g, c = 0, res;   
    while ( res = re.exec( text ) ) if ( ++c === 10 || res[1].slice( -1 ) === '.' ) break;

    var p = re.lastIndex;
    return '<span class="easing">' + text.slice( 0, p ) + '</span>' + text.slice( p );  
});

Live demo: http://jsfiddle.net/3DaEV/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM