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

102

Я не очень хорошо разбираюсь в манипуляциях со строками в JavaScript, и мне было интересно, как можно сократить строку, не обрезая ни слова. Я знаю, как использовать substring, но не indexOf или что-то еще.

Скажем, у меня была следующая строка:

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

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

"Это длинная строка, которую я не могу"

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

Джош Бедо
источник
вы имеете в виду обрезать строку? попробуйте" too many spaces ".trim()
Anurag
1
Некоторые примеры входных данных и ожидаемых выходных данных очень помогут в ответе на этот вопрос.
deceze
хорошо, извините, у меня была строка 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(" ")))
}
NT3RP
источник
9
@josh это абсолютно неправда, что ".replace" не работает в "функциях jQuery". Нет даже такой вещи, как «функция jQuery».
Pointy
3
разве это не должно быть «maxLength + 1». И если maxLength больше или равна полной длине предложения, последнее слово не включается. но спасибо за решение.
Бейтан Курт
4
Если использовать это в строке, которая короче maxLength, последнее слово обрезается. Возможно, @AndrewJuniorHoward уже указал исправление для this ( maxLength + 1), но я исправил его, просто добавив эту строку вверху:var yourString += " ";
tylerl
3
К сожалению, если убрать fox jumps over the lazy dogдеталь, результат будет The quick brown , когда и положено The quick brown fox.
Андрей Гордеев
2
Это всегда режет последнее слово.
Крис Чинелли
109

Есть много способов сделать это, но регулярное выражение - полезный однострочный метод:

"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 У вас должна быть возможность сделать максимальную длину переменной с помощью объекта regexp: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 (). Хороший улов!
Tici
2
Это происходит сбой , если по какой - то причине strэто undefined. Я добавилif (!str || str.length <= maxLen) return str;
Silvain
это не обрабатывает
крайний
@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
*/
Kennebec
источник
Обратите внимание, что 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, который не обрабатывает некоторые угловые случаи, я сделал этот код. Это гарантирует, что текст с событием size> 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 05
6

Вот решение в одну строку.

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

function shorten(text,max) {
    return text && text.length > max ? text.slice(0,max).split(' ').slice(0, -1).join(' ') : text
}


console.log(shorten(text,10));

Йоаким Поромаа Хельгер
источник
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
эээхм. Думаю, это единственно правильный ответ на вопрос. - спросил он, не прерывая слова.
Майк Арон
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 = ['Паста', 'с'];

третья итерация:

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 (' '), которые могут сделать строку длиннее предела. Если вы измените функцию reduce () на (acc, cur, idx), а if - на (acc + 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--;
}

Bbeckford
источник
0

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

    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);

В выводе этого фрагмента LIMIT равно 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 / library.

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)
Лэндон Колл
источник