Скоротіть рядок без вирізання слів у JavaScript


102

Я не дуже добре маніпулюю рядками в JavaScript, і мені було цікаво, як би ви скоротили рядок, не відрізавши жодного слова. Я знаю, як використовувати підрядку, але не indexOf або щось дуже добре.

Скажімо, у мене був такий рядок:

text = "this is a long string I cant display"

Я хочу обрізати його до 10 символів, але якщо це не закінчується пробілом, закінчіть слово. Я не хочу, щоб змінна рядка виглядала так:

"це довга струна, яку я не можу"

Я хочу, щоб воно закінчило слово, поки не з’явиться пробіл.


ви маєте на увазі обрізати рядок? спробуйте" too many spaces ".trim()
Anurag

1
Деякі приклади вхідних даних та очікуваний вихід допоможуть багато у відповіді на це питання.
деге

добре, вибачте, кажу, що у мене був рядок text = "це довгий рядок, який я не можу показати", я хочу обрізати його до 10 символів, але якщо він не закінчується пробілом, закінчуйте слово, я не хочу, щоб змінна рядка виглядала як це "це довга струна, яку я не можу розірвати"
Джош Бедо

Відповіді:


180

Якщо я правильно розумію, ви хочете скоротити рядок до певної довжини (наприклад, скоротити "The quick brown fox jumps over the lazy dog", скажімо, до 6 символів, не відрізаючи жодного слова).

Якщо це так, ви можете спробувати щось на зразок наступного:

var yourString = "The quick brown fox jumps over the lazy dog"; //replace with your string.
var maxLength = 6 // maximum number of characters to extract

//Trim and re-trim only when necessary (prevent re-trim when string is shorted than maxLength, it causes last word cut) 
if(yourString.length > trimmedString.length){
    //trim the string to the maximum length
    var trimmedString = yourString.substr(0, maxLength);

    //re-trim if we are in the middle of a word and 
    trimmedString = trimmedString.substr(0, Math.min(trimmedString.length, trimmedString.lastIndexOf(" ")))
}

9
@josh, це абсолютно неправда, що ".replace" не працює в "функціях jQuery". Існує навіть не таке поняття, як "функція jQuery".
Pointy

3
хіба це не повинно бути "maxLength + 1". І якщо maxLength більше або дорівнює повній довжині речення, останнє слово не включається. але дякую за рішення.
Бейтан Курт,

4
Якщо використовувати це для рядка, який коротший за maxLength, останнє слово обрізається. Можливо, @AndrewJuniorHoward вже заявив виправлення цього ( maxLength + 1), але я це виправив, просто додавши цю лінію вгору:var yourString += " ";
tylerl

3
На жаль, якщо ви заберете fox jumps over the lazy dogчастину, результат буде The quick brown , коли він повинен бути The quick brown fox.
Андрій Гордєєв

2
Це завжди скорочує останнє слово.
Кріс Сінеллі

108

Існує маса способів зробити це, але регулярний вираз є корисним методом в одному рядку:

"this is a longish string of text".replace(/^(.{11}[^\s]*).*/, "$1"); 
//"this is a longish"

Цей вираз повертає перші 11 (будь-які) символи плюс будь-які наступні символи, що не пробіл.

Приклад сценарію:

<pre>
<script>
var t = "this is a longish string of text";

document.write("1:   " + t.replace(/^(.{1}[^\s]*).*/, "$1") + "\n");
document.write("2:   " + t.replace(/^(.{2}[^\s]*).*/, "$1") + "\n");
document.write("5:   " + t.replace(/^(.{5}[^\s]*).*/, "$1") + "\n");
document.write("11:  " + t.replace(/^(.{11}[^\s]*).*/, "$1") + "\n");
document.write("20:  " + t.replace(/^(.{20}[^\s]*).*/, "$1") + "\n");
document.write("100: " + t.replace(/^(.{100}[^\s]*).*/, "$1") + "\n");
</script>

Вихід:

1:   this
2:   this
5:   this is
11:  this is a longish
20:  this is a longish string
100: this is a longish string of text

Приголомшливо, я буквально погуглив це питання мільйоном способів і міг знайти лише робочу версію для php, нічого близького до цього, що включає цикли.
Джош Бедо

1
Це стосується першого (і єдиного, в даному випадку) відповідного підвиразу - матеріалу в дужках. $ 0 відноситься до цілого збігу, а в цьому випадку - до цілого рядка.
Гаміш

3
@josh Ви повинні мати змогу зробити максимальну довжину змінною за допомогою об'єкта регулярного виразу:t.replace(new RegExp("^(.{"+length+"}[^\s]*).*"), "$1")
rjmackay

1
@Hamish ваш варіант працює добре, але він включає останнє слово також, якщо довжина перевищує. Я спробував змінити вираз регулярного виразу, щоб виключити останнє слово, якщо максимальне обмеження слів перевищує, але це не спрацювало. Як ми можемо цього досягти?
Шашанк Агравал

1
Ну, це насправді працює неправильно, іноді я передаю максимальне значення, наприклад, якщо останнє слово вже складало 30 символів, воно буде довжиною більше 60! навіть якщо для нього встановлено довжину{30}
Аль-Мотафар

65

Я здивований, що на таку просту проблему, як ця, є так багато відповідей, які важко прочитати, а деякі, включаючи обрану, не працюють.

Зазвичай я хочу, щоб рядок результатів був не більше maxLen символів. Я також використовую цю саму функцію, щоб скоротити кулі в URL-адресах.

str.lastIndexOf(searchValue[, fromIndex]) приймає другий параметр, який є індексом, за яким слід починати пошук назад у рядку, роблячи речі ефективними та простими.

// Shorten a string to less than maxLen characters without truncating words.
function shorten(str, maxLen, separator = ' ') {
  if (str.length <= maxLen) return str;
  return str.substr(0, str.lastIndexOf(separator, maxLen));
}

Це вибірковий вихід:

for (var i = 0; i < 50; i += 3) 
  console.log(i, shorten("The quick brown fox jumps over the lazy dog", i));

 0 ""
 3 "The"
 6 "The"
 9 "The quick"
12 "The quick"
15 "The quick brown"
18 "The quick brown"
21 "The quick brown fox"
24 "The quick brown fox"
27 "The quick brown fox jumps"
30 "The quick brown fox jumps over"
33 "The quick brown fox jumps over"
36 "The quick brown fox jumps over the"
39 "The quick brown fox jumps over the lazy"
42 "The quick brown fox jumps over the lazy"
45 "The quick brown fox jumps over the lazy dog"
48 "The quick brown fox jumps over the lazy dog"

А для слизи:

for (var i = 0; i < 50; i += 10) 
  console.log(i, shorten("the-quick-brown-fox-jumps-over-the-lazy-dog", i, '-'));

 0 ""
10 "the-quick"
20 "the-quick-brown-fox"
30 "the-quick-brown-fox-jumps-over"
40 "the-quick-brown-fox-jumps-over-the-lazy"

1
Я зовсім забув про lastIndexOf (). Гарний улов!
Тичі

2
Це збоїв, якщо з якихось причин strє undefined. Я додавif (!str || str.length <= maxLen) return str;
Сільвейн

це не обробляє
крайній

@shrewquest Це працює. Якщо роздільника немає в рядку, він повертає сам рядок if str.length <= maxLen. В іншому випадку він повертає порожній рядок.
Кріс Сінеллі

20

Кожен, здається, забуває, що indexOf бере два аргументи - рядок, який відповідає, та символьний індекс, з якого слід шукати. Ви можете розірвати рядок на першому пробілі через 10 символів.

function cutString(s, n){
    var cut= s.indexOf(' ', n);
    if(cut== -1) return s;
    return s.substring(0, cut)
}
var s= "this is a long string i cant display";
cutString(s, 10)

/*  returned value: (String)
this is a long
*/

Зауважте, що indexOf можна замінити lastIndexOf, якщо потрібні жорсткі межі.
Scheintod

14

Lodash має спеціально написану для цього функцію: _.truncate

const truncate = _.truncate
const str = 'The quick brown fox jumps over the lazy dog'

truncate(str, {
  length: 30, // maximum 30 characters
  separator: /,?\.* +/ // separate by spaces, including preceding commas and periods
})

// 'The quick brown fox jumps...'

7

На основі відповіді NT3RP, яка не обробляє деякі кутові випадки, я створив цей код. Це гарантує не повертати текст з розміром> maxLength подія, ...в кінці додано еліпсис .

Це також обробляє деякі кутові випадки, такі як текст, у якому є одне слово> maxLength

shorten: function(text,maxLength,options) {
    if ( text.length <= maxLength ) {
        return text;
    }
    if ( !options ) options = {};
    var defaultOptions = {
        // By default we add an ellipsis at the end
        suffix: true,
        suffixString: " ...",
        // By default we preserve word boundaries
        preserveWordBoundaries: true,
        wordSeparator: " "
    };
    $.extend(options, defaultOptions);
    // Compute suffix to use (eventually add an ellipsis)
    var suffix = "";
    if ( text.length > maxLength && options.suffix) {
        suffix = options.suffixString;
    }

    // Compute the index at which we have to cut the text
    var maxTextLength = maxLength - suffix.length;
    var cutIndex;
    if ( options.preserveWordBoundaries ) {
        // We use +1 because the extra char is either a space or will be cut anyway
        // This permits to avoid removing an extra word when there's a space at the maxTextLength index
        var lastWordSeparatorIndex = text.lastIndexOf(options.wordSeparator, maxTextLength+1);
        // We include 0 because if have a "very long first word" (size > maxLength), we still don't want to cut it
        // But just display "...". But in this case the user should probably use preserveWordBoundaries:false...
        cutIndex = lastWordSeparatorIndex > 0 ? lastWordSeparatorIndex : maxTextLength;
    } else {
        cutIndex = maxTextLength;
    }

    var newText = text.substr(0,cutIndex);
    return newText + suffix;
}

Я думаю, ви можете легко усунути залежність від jquery, якщо це вас турбує.


3
Мені подобається це рішення, але чи не слід передавати аргументи, щоб $.extendповернути їх назад?
JKesMc9tqIQe9M


3

Я запізнююсь на вечірку, але ось невелике та просте рішення, яке я придумав, щоб повернути кількість слів.

Це не пов’язано безпосередньо з вашими вимогами до персонажів , але воно забезпечує той самий результат, який, на мою думку, вам домагався.

function truncateWords(sentence, amount, tail) {
  const words = sentence.split(' ');

  if (amount >= words.length) {
    return sentence;
  }

  const truncated = words.slice(0, amount);
  return `${truncated.join(' ')}${tail}`;
}

const sentence = 'Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.';

console.log(truncateWords(sentence, 10, '...'));

Дивіться робочий приклад тут: https://jsfiddle.net/bx7rojgL/


Ви написали функцію JS, яка скорочує рядок до кількох слів. Прочитайте питання ще раз.
ChristoKiwi

1
ееехм. Я думаю, що це єдина правильна відповідь на питання. - спитав він, не ріжучи слова.
Mike Aron

2

Це виключає остаточне слово, а не включає його.

function smartTrim(str, length, delim, appendix) {
    if (str.length <= length) return str;

    var trimmedStr = str.substr(0, length+delim.length);

    var lastDelimIndex = trimmedStr.lastIndexOf(delim);
    if (lastDelimIndex >= 0) trimmedStr = trimmedStr.substr(0, lastDelimIndex);

    if (trimmedStr) trimmedStr += appendix;
    return trimmedStr;
}

Використання:

smartTrim(yourString, 11, ' ', ' ...')
"The quick ..."

2

Я застосував інший підхід. Хоча мені потрібен був подібний результат, я хотів, щоб моє повертане значення було менше вказаної довжини.

function wordTrim(value, length, overflowSuffix) {
    value = value.trim();
    if (value.length <= length) return value;
    var strAry = value.split(' ');
    var retString = strAry[0];
    for (var i = 1; i < strAry.length; i++) {
        if (retString.length >= length || retString.length + strAry[i].length + 1 > length) break;
        retString += " " + strAry[i];
    }
    return retString + (overflowSuffix || '');
}

Редагувати Я трохи його переробив тут: Приклад JSFiddle . Він повторно приєднується до вихідного масиву, замість об'єднання.

function wordTrim(value, length, overflowSuffix) {
    if (value.length <= length) return value;
    var strAry = value.split(' ');
    var retLen = strAry[0].length;
    for (var i = 1; i < strAry.length; i++) {
        if(retLen == length || retLen + strAry[i].length + 1 > length) break;
        retLen+= strAry[i].length + 1
    }
    return strAry.slice(0,i).join(' ') + (overflowSuffix || '');
}

2
function shorten(str,n) {
  return (str.match(RegExp(".{"+n+"}\\S*"))||[str])[0];
}

shorten("Hello World", 3); // "Hello"


1

Ви можете використовувати truncateоднокласник нижче:

const text = "The string that I want to truncate!";

const truncate = (str, len) => str.substring(0, (str + ' ').lastIndexOf(' ', len));

console.log(truncate(text, 14));


1
shorten(str, maxLen, appendix, separator = ' ') {
if (str.length <= maxLen) return str;
let strNope = str.substr(0, str.lastIndexOf(separator, maxLen));
return (strNope += appendix);

}

var s = "це довгий рядок, і я не можу пояснити все"; скоротити (s, 10, '...')

/* "це .." */


1

Ось ще один фрагмент коду, який обрізає розділові знаки (шукав це, і Google знайшов це питання тут). Довелося придумати рішення самостійно, тож ось що я зламав за 15 хвилин. Знайде всі випадки. ! ? і скорочує в будь-якому їх положенні, яке <ніжlen

function pos(str, char) {
    let pos = 0
    const ret = []
    while ( (pos = str.indexOf(char, pos + 1)) != -1) {
        ret.push(pos)
    }
    return ret
}

function truncate(str, len) {
    if (str.length < len)
        return str

    const allPos = [  ...pos(str, '!'), ...pos(str, '.'), ...pos(str, '?')].sort( (a,b) => a-b )
    if (allPos.length === 0) {
        return str.substr(0, len)
    }

    for(let i = 0; i < allPos.length; i++) {
        if (allPos[i] > len) {
            return str.substr(0, allPos[i-1] + 1)
        }
    }
}

module.exports = truncate

1

Машинопис, і з еліпсами :)

export const sliceByWord = (phrase: string, length: number, skipEllipses?: boolean): string => {
  if (phrase.length < length) return phrase
  else {
    let trimmed = phrase.slice(0, length)
    trimmed = trimmed.slice(0, Math.min(trimmed.length, trimmed.lastIndexOf(' ')))
    return skipEllipses ? trimmed : trimmed + '…'
  }
}

1

'Макарони з помідорами та шпинатом'

якщо ви не хочете скорочувати слово навпіл

перша ітерація:

acc: 0 / acc + cur.length = 5 / newTitle = ['Паста'];

друга ітерація:

acc: 5 / acc + cur.length = 9 / newTitle = ['Pasta', 'with'];

третя ітерація:

acc: 9 / acc + cur.length = 15 / newTitle = ['Макаронні вироби', 'з', 'помідор'];

четверта ітерація:

acc: 15 / acc + cur.length = 18 (обмежений ліміт) / newTitle = ['Макаронні вироби', 'з', 'помідор'];

const limitRecipeTitle = (title, limit=17)=>{
    const newTitle = [];
    if(title.length>limit){
        title.split(' ').reduce((acc, cur)=>{
            if(acc+cur.length <= limit){
                newTitle.push(cur);
            }
            return acc+cur.length;
        },0);
    }

    return `${newTitle.join(' ')} ...`
}

вихід: Макаронні вироби з помідорами ...


Це не враховує символів 'join (' '), які можуть робити рядок довше обмеження. Якщо ви зміните функцію зменшення () на (acc, cur, idx) і if на (ac + cur.length <= limit - idx), вона буде враховувати додаткові пробіли, коли слова з’єднуються назад. Якщо це суворо дозволено, потрібно.
PSaul

0

Для чого варто, я написав це, щоб усікати до межі слова, не залишаючи розділові знаки або пробіли в кінці рядка:

function truncateStringToWord(str, length, addEllipsis)
{
    if(str.length <= length)
    {
        // provided string already short enough
        return(str);
    }

    // cut string down but keep 1 extra character so we can check if a non-word character exists beyond the boundary
    str = str.substr(0, length+1);

    // cut any non-whitespace characters off the end of the string
    if (/[^\s]+$/.test(str))
    {
        str = str.replace(/[^\s]+$/, "");
    }

    // cut any remaining non-word characters
    str = str.replace(/[^\w]+$/, "");

    var ellipsis = addEllipsis && str.length > 0 ? '&hellip;' : '';

    return(str + ellipsis);
}

var testString = "hi stack overflow, how are you? Spare";
var i = testString.length;

document.write('<strong>Without ellipsis:</strong><br>');

while(i > 0)
{
  document.write(i+': "'+ truncateStringToWord(testString, i) +'"<br>');
  i--;
}

document.write('<strong>With ellipsis:</strong><br>');

i = testString.length;
while(i > 0)
{
  document.write(i+': "'+ truncateStringToWord(testString, i, true) +'"<br>');
  i--;
}


0

Не знайшов задовільних рішень. Тож я написав щось таке, яке є загальним, і працює як перша, так і остання частина вашого тексту (щось на кшталт підстрочки, але для слів). Також ви можете встановити, чи хочете ви, щоб пробіли не залишались у підрахунку символів.

    function chopTxtMinMax(txt, firstChar, lastChar=0){
        var wordsArr = txt.split(" ");
        var newWordsArr = [];

        var totalIteratedChars = 0;
        var inclSpacesCount = true;

        for(var wordIndx in wordsArr){
            totalIteratedChars += wordsArr[wordIndx].length + (inclSpacesCount ? 1 : 0);
            if(totalIteratedChars >= firstChar && (totalIteratedChars <= lastChar || lastChar==0)){
                newWordsArr.push(wordsArr[wordIndx]);
            }
        }

        txt = newWordsArr.join(" ");
        return txt;
    }

0

Я запізнився на це, але я думаю, що ця функція робить саме те, що запитує OP. Ви можете легко змінити значення SENTENCE і LIMIT для різних результатів.

function breakSentence(word, limit) {
  const queue = word.split(' ');
  const list = [];

  while (queue.length) {
    const word = queue.shift();

    if (word.length >= limit) {
      list.push(word)
    }
    else {
      let words = word;

      while (true) {
        if (!queue.length ||
            words.length > limit ||
            words.length + queue[0].length + 1 > limit) {
          break;
        }

        words += ' ' + queue.shift();
      }

      list.push(words);
    }
  }

  return list;
}

const SENTENCE = 'the quick brown fox jumped over the lazy dog';
const LIMIT = 11;

// get result
const words = breakSentence(SENTENCE, LIMIT);

// transform the string so the result is easier to understand
const wordsWithLengths = words.map((item) => {
  return `[${item}] has a length of - ${item.length}`;
});

console.log(wordsWithLengths);

Результатом цього фрагмента є де ЛІМІТ 11 - це:

[ '[the quick] has a length of - 9',
  '[brown fox] has a length of - 9',
  '[jumped over] has a length of - 11',
  '[the lazy] has a length of - 8',
  '[dog] has a length of - 3' ]

0

З граничними умовами, як порожнє речення та дуже довге перше слово. Крім того, він не використовує специфічні для мови строкові api / бібліотеки.

function solution(message, k) {
    if(!message){
        return ""; //when message is empty
    }
    const messageWords = message.split(" ");
    let result = messageWords[0];
    if(result.length>k){
        return ""; //when length of first word itself is greater that k
    }
    for(let i = 1; i<messageWords.length; i++){
        let next = result + " " + messageWords[i];

        if(next.length<=k){
            result = next;
        }else{
            break;
        }
    }
    return result;
}

console.log(solution("this is a long string i cant display", 10));


-1

Ви можете обрізати пробіли за допомогою цього:

var trimmedString = flabbyString.replace(/^\s*(.*)\s*$/, '$1');

-1

Оновлено від @ NT3RP Я виявив, що якщо струна вперше потрапить на пробіл навколо неї, в кінцевому підсумку буде видалено це слово, зробивши рядок на одне слово коротшим, ніж це може бути. Тому я просто кинув оператор if else, щоб перевірити, чи не maxLength не потрапляє на пробіл.

codepen.io

var yourString = "The quick brown fox jumps over the lazy dog"; //replace with your string.
var maxLength = 15 // maximum number of characters to extract

if (yourString[maxLength] !== " ") {

//trim the string to the maximum length
var trimmedString = yourString.substr(0, maxLength);

alert(trimmedString)

//re-trim if we are in the middle of a word
trimmedString = trimmedString.substr(0, Math.min(trimmedString.length, trimmedString.lastIndexOf(" ")))
}

else {
  var trimmedString = yourString.substr(0, maxLength);
}

alert(trimmedString)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.