web-dev-qa-db-fra.com

Raccourcir la chaîne sans couper les mots en JavaScript

Je ne suis pas très doué avec la manipulation de chaînes en JavaScript, et je me demandais comment raccourcir une chaîne sans couper aucun mot. Je sais comment utiliser la sous-chaîne, mais pas indexOf ou quoi que ce soit vraiment bien.

Disons que j'avais la chaîne suivante:

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

Je veux le réduire à 10 caractères, mais s'il ne se termine pas par un espace, terminez le mot. Je ne veux pas que la variable chaîne ressemble à ceci:

"c'est une longue chaîne que je ne peux pas dis"

Je veux qu'il termine la Parole jusqu'à ce qu'il y ait un espace.

68
Josh Bedo

Si je comprends bien, vous souhaitez raccourcir une chaîne à une certaine longueur (par exemple, raccourcissez "The quick brown fox jumps over the lazy dog" à, disons, 6 caractères sans couper aucun mot).

Si tel est le cas, vous pouvez essayer l'une des solutions suivantes:

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 the string to the maximum length
var trimmedString = yourString.substr(0, maxLength);

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

Il existe de nombreuses façons de le faire, mais une expression régulière est une méthode utile à une ligne:

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

Cette expression renvoie les 11 premiers caractères (tous) plus les éventuels caractères non-espace suivants.

Exemple de script:

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

Sortie:

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
81
Hamish

Je suis un peu surpris que pour un problème simple comme celui-ci, il y ait tellement de réponses difficiles à lire que certaines, y compris celle choisie, ne fonctionnent pas. 

Je veux généralement que la chaîne de résultat soit au plusmaxLen caractères. J'utilise également cette même fonction pour raccourcir les slugs dans les URL. 

str.lastIndexOf(searchValue[, fromIndex]) prend un deuxième paramètre qui est l'index auquel commencer la recherche en arrière dans la chaîne, ce qui rend les choses simples et efficaces.

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

Ceci est un exemple de sortie:

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"

Et pour la limace:

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"
45
Chris Cinelli

Tout le monde semble oublier qu'indexOf prend deux arguments: la chaîne à rechercher et l'index de caractère à partir duquel chercher. Vous pouvez rompre la chaîne au premier espace après 10 caractères.

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
*/
18
kennebec

Lodash a une fonction spécifiquement écrite pour cela: _.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...'
9
Leon Li

Sur la base de la réponse NT3RP qui ne gère pas certains cas, j'ai créé ce code . Il garantit de ne pas renvoyer de texte avec un événement size> maxLength. Un Ellipsis ... a été ajouté à la fin.

Cela gère également certains cas d'angle, comme un texte qui a un seul mot étant> 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;
}

Je suppose que vous pouvez facilement supprimer la dépendance jquery si cela vous dérange.

7
Sebastien Lorber

J'ai pris une approche différente. Alors que j'avais besoin d'un résultat similaire, je voulais garder ma valeur de retour inférieure à la longueur spécifiée.

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

Edit Je l'ai un peu refactoré ici: Exemple JSFiddle . Il rejoint le tableau d'origine au lieu de concaténer. 

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
Pete
function shorten(str,n) {
  return (str.match(RegExp(".{"+n+"}\\S*"))||[str])[0];
}

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

// SHORTEN STRING TO WHOLE WORDS
function shorten(s,l) {
  return (s.match(new RegExp(".{"+l+"}\\S*"))||[s])[0];
}

console.log( shorten("The quick brown fox jumps over the lazy dog", 6) ); // "The quick"

2
Roko C. Buljan

Cela exclut le mot final au lieu de l'inclure.

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

Usage:

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

Je suis en retard à la fête, mais voici une solution simple et petite que j'ai proposée pour renvoyer une quantité de mots. 

Ce n'est pas directement lié à votre besoin de caractères, mais il sert le même résultat que je crois que vous cherchiez.

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, '...'));

Voir l'exemple de travail ici: https://jsfiddle.net/bx7rojgL/

1

Voici encore un autre morceau de code qui tronque le long de signes de ponctuation (cherchait ceci et Google a trouvé cette question ici). J'ai dû trouver une solution par moi-même, c'est donc ce que j'ai piraté en 15 minutes. Trouve toutes les occurrences de. ! ? et tronque à n'importe quelle position de ceux-ci <à 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
Stefan

Vous pouvez utiliser truncate one-liner ci-dessous:

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

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

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

0
Viktor Vlasenko

Voici une solution en une ligne.

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

Pour ce que ça vaut, j’écris ceci: tronquer au bord de Word sans laisser de ponctuation ni d’espace à la fin de la chaîne:

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
bbeckford
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 = "c'est une longue chaîne et je ne peux pas tout expliquer"; raccourcir (s, 10, '...')

/* "c'est .." */

0
vivi margaretha

Avec des conditions aux limites comme une phrase vide et un très long premier mot. En outre, il n'utilise aucune chaîne spécifique à la langue 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));
0
Shishir Arora

Je suis arrivé en retard pour cela mais je pense que cette fonction fait exactement ce que demande OP. Vous pouvez facilement modifier les valeurs SENTENCE et LIMIT pour obtenir des résultats différents.

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

La sortie de cet extrait est où la limite est de 11 est:

[ '[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
Ian Calderon

Vous pouvez couper des espaces avec ceci:

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

Je n'ai pas trouvé les solutions votées satisfaisantes. J'ai donc écrit quelque chose qui est un peu générique et qui fonctionne à la fois en première et dernière partie de votre texte (quelque chose comme substrat mais pour les mots). Vous pouvez également définir si vous souhaitez que les espaces soient omis dans le décompte des caractères.

    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
Vasili Paspalas