Skip to content

如何使用JavaScript将字符串截断为指定长度?

将字符串拆分为单词并不容易,找到一个好的地方来截断字符串也不容易。问题的一部分在于识别单词边界和单词本身。更高级的用例甚至可能需要考虑区域设置。让我们从最简单的情况逐步构建到最高级的情况。

截断字符串

最简单的截断字符串的方法是使用String.prototype.slice()。你只需要将String.prototype.length与所需长度进行比较,并返回截断到所需长度的字符串。如果字符串长度小于所需长度,则返回原字符串。

const truncateString = (str, num) =>
  str.length > num ? str.slice(0, num) : str;

truncateString('boomerang', 6); // 'boomer'

截断字符串,并添加省略号

这样做是可以的,但结果并没有提示字符串已被截断的事实。让我们在字符串末尾添加省略号,以表示字符串已被截断。如果字符串已被截断,我们需要将'...'附加到字符串末尾。我们还需要考虑省略号的长度,因此需要从所需长度中减去省略号的长度。

const truncateString = (str, lim) =>
  str.length > lim ? str.slice(0, lim > 3 ? lim - 3 : lim) + '...' : str;

truncateString('boomerang', 7); // 'boom...'

在空格处截断字符串

到目前为止,我们一直在指定的长度处截断字符串,无论它是否在单词中间。但是您可能需要尊重单词边界,并在空格字符处截断字符串。

我们可以使用String.prototype.lastIndexOf()来找到所需长度下最后一个空格的索引。然后,我们可以使用String.prototype.slice()根据最后一个空格的索引适当地截断字符串,尽可能地保留空格,并在末尾添加'...'

const truncateStringAtWhitespace = (str, lim, ending = '...') => {
  if (str.length <= lim) return str;
  const lastSpace = str.slice(0, lim - ending.length + 1).lastIndexOf(' ');
  return str.slice(0, lastSpace > 0 ? lastSpace : lim - ending.length) + ending;
};

truncateStringAtWhitespace('short', 10); // 'short'
truncateStringAtWhitespace('not so short', 10); // 'not so...'
truncateStringAtWhitespace('trying a thing', 10); // 'trying...'
truncateStringAtWhitespace('javascripting', 10); // 'javascr...'

区域敏感的字符串截断

最后,我们来到了最复杂的问题 - 区域敏感的字符串截断。这是一个难以解决的问题,这就是为什么JavaScript友好地添加了Intl.Segmenter对象。

Intl.Segmenter允许您指定区域设置和granularity选项来指定如何分段字符串granularity选项可以设置为'grapheme''word''sentence',根据需要进行设置。在字符串上使用Intl.Segmenter.prototype.segment()将返回一个可迭代的Segments对象。然后可以使用它来找到正确的索引来分割字符串,而不会在单词或句子中间。

const truncateStringAtWord = (str, lim, locale = 'en-US', ending = '...') => {
  const segmenter = new Intl.Segmenter(locale, { granularity: 'word' });
  let lastWordBreak = -1;

  for (let word of segmenter.segment(str)) {
    if (word.isWordLike) continue;
    if (word.index >= lim) break;
    lastWordBreak = word.index;
  }

```javascript
const truncateStringAtWord = (str, lim, ending = '...') => {
  let lastWordBreak = -1;

  for (let i = 0; i < str.length; i++) {
    if (str[i] === ' ') {
      if (i >= lim) break;
      lastWordBreak = i;
    }
  }

  if (lastWordBreak === -1) return str;

  return str.slice(0, lastWordBreak) + ending;
};

const truncateStringAtSentence = (
  str,
  lim,
  locale = 'en-US',
  ending = '...'
) => {
  const segmenter = new Intl.Segmenter(locale, { granularity: 'sentence' });
  let lastSentenceBreak = -1;

  for (let sentence of segmenter.segment(str)) {
    if (
      lastSentenceBreak !== -1 &&
      sentence.index + sentence.segment.length >= lim
    )
      break;
    lastSentenceBreak = sentence.index + sentence.segment.length;
  }

  return str.slice(0, lastSentenceBreak).trim().slice(0, -1) + ending;
};

const str =
  'The quick brown fox jumps over the lazy dog. The jay, pig, fox, zebra and my wolves quack!';
const lim = 50;

truncateStringAtWord(str, lim);
// 'The quick brown fox jumps over the lazy dog. The...'
truncateStringAtSentence(str, lim);
// 'The quick brown fox jumps over the lazy dog...'

[!NOTE]

Intl.Segmenter 对象在现代浏览器和 Node.js(自 v16.0.0 起)中可用。请确保检查目标环境的兼容性。