【问题标题】:Truncate strings without break words截断不带断词的字符串
【发布时间】:2021-10-28 05:37:54
【问题描述】:

在过去的几个小时里,我一直在尝试将我在使用该语言时使用的 PHP 代码移植到 JavaScript,以便截断用点(或任何其他字符串)插入它们的字符串,但仅部分成功。

这是大约四年前开发的三种已经可以工作的算法——我自己做了两个,最后一个更复杂,I had a big help

function truncateBefore( $string, $length, $replacement ) {
    return substr( $string, 0, strrpos( substr( $string, 0, $length ), ' ' ) ) . $replacement;
}

function truncateAfter( $string, $length, $replacement ) {
    return substr( $string, 0, ( strpos( substr( $string, $length ),' ' ) + $length ) ) . $replacement;
}

function truncateMiddle( $string, $length, $replacement ) {

    $len = (int) ( ( $length - strlen( $replacement ) ) / 2 );

    // Separate the output from wordwrap() into an array of lines

    $segments = explode( "\n", wordwrap( $string, $len ) ) ;

    $end = end( $segments );

    if( strlen( $end ) <= ( $length / 2 ) && count( $segments ) > 2 ) {

        $prev = explode( ' ', prev( $segments ) );

        while( strlen( $end ) <= ( $length / 2 ) ) {
            $end = sprintf( '%s %s', array_pop( $prev ), $end );
        }
    }

    return new Strings(
        [ 'value' => reset( $segments ) . $replacement . trim( $end ) ]
    );
}

与此示例字符串一起使用:

$string = 'Lorem ipsum labore ad in consequat laboris in mollit
fugiat et do laborum aliqua laborum mollit amet laborum
duis irure irure ut aute pariatur pariatur duis dolore
in sed nisi occaecat officia nisi et esse ut magna et.';

他们分别返回:

Lorem ipsum labore ad in... // Before the "consequat"
Lorem ipsum labore ad in consequat... // After the "consequat"
Lorem ipsum...esse ut magna et. // Without breaking words, first 30 and last 30

现在我解释了一切,关于我当前的问题(JavaScript),这些是我的尝试:

function truncateBefore( str, length, delimiter ) {
    return str.substring( 0, str.lastIndexOf( ' ', length ) ) + delimiter;
}

function truncateAfter( str, length, delimiter ) {
    return str.substring( 0, ( str.indexOf( ' ', str.substring( length ) ) + length ) ) + delimiter;
}

function truncateMiddle( str, $length, delimiter ) {
    // Nothing :(
}

前两个就像一个魅力(当然,我可以测试)。花在阅读 MDN 上的时间得到了回报,但第三次我什至无法开始,因为它依赖于一些 JS 没有的东西。

当然,我可以找到“polyfills”,事实上,我确实找到了(例如wordwrap()),但最后,end()reset()prev() 胜过我。

试图求助于 PHP.JS 项目,却发现它现在被称为 Locutus,更大而且有些复杂,没有即插即用的能力......初次见面。

我怎样才能做到这一点?

【问题讨论】:

    标签: javascript php portability


    【解决方案1】:

    也许你可以尝试这样的事情:

    function truncateMiddle(str, length, delim) {
    
        // Replacing line breaks with spaces and splitting the string on spaces
        const words = str.replace("\n", " ").split(" ");
    
        // Final string variable
        let finalString = "";
    
        // Utility variables for the loops
        let letterCount = 0;
        let wordIndex = 0;
    
        // Adding `length` characters without splitting words.
        while (letterCount <= length) {
            finalString += `${words[wordIndex]} `;
            letterCount += words[wordIndex].length;
            ++wordIndex;
        }
    
        // Removing last space
        finalString.substr(0, finalString.length - 1);
    
        // Adding the delimiter
        finalString += delim;
    
        // Resetting utility variables
        letterCount = 0;
        wordIndex = words.length - 1;
    
        // Array storing the end words (will be reversed afterwards)
        const wordsEnd = [];
    
        // Adding `length` characters from the end, without splitting words
        while (letterCount <= length) {
            wordsEnd.push(words[wordIndex]);
            letterCount += words[wordIndex].length;
            --wordIndex;
        }
    
        // Adding the reversed array of words
    
        for (const word of wordsEnd.reverse()) {
            finalString += `${word} `;
        }
    
        return finalString;
    }
    
    console.log(truncateMiddle(
        "Lorem ipsum labore ad in consequat laboris in mollit\n" +
        "fugiat et do laborum aliqua laborum mollit amet laborum\n" +
        "duis irure irure ut aute pariatur pariatur duis dolore\n" +
        "in sed nisi occaecat officia nisi et esse ut magna et.",
        30,
        " ... "
    ));

    它可能会使用一些优化,但它做得很好。

    【讨论】:

    • 另外两个呢?我是否正确移植了它们?因为我在较短的字符串上存在一些差异(断字),而原始代码不会发生这种情况
    【解决方案2】:

    您可以使用truncateBefore()truncateAfter() 函数来实现truncateMiddle()。为了获得字符串的最后 N 个字符,我将单词拆分为数组(空白字符是分隔符),反转它然后再次连接单词(使用空白字符作为胶水);我对返回字符串应用相同的过程。

    truncateMiddle() 函数说明: if (length parameter) > (str.length / 2) 那么有些单词会重复出现(相同的单词会出现在前N个字符和最后一个N 个字符)。

    function truncateBefore(str, length, delimiter) {
      return str.substring(0, str.lastIndexOf(' ', length)) + delimiter;
    }
    
    function truncateAfter(str, length, delimiter) {
        return str.substring(0, (str.indexOf(' ', str.substring(length)) + length)) + delimiter;
    }
    
    /**
     * Return first and last N characters of a string without
     * break any word.
    */
    function truncateMiddle(str, length, delimiter) {  
      return truncateAfter(str, length, '') 
              + delimiter 
              + truncateAfter(str.split(' ').reverse().join(' '), length, '').split(' ').reverse().join(' ');
    }
    
    let res1 = truncateBefore("Lorem ipsum labore ad in consequat laboris in mollit fugiat et do laborum aliqua laborum mollit amet laborum duis irure irure ut aute pariatur pariatur duis dolore in sed nisi occaecat officia nisi et esse ut magna et.", 30, " ... ");
    
    let res2 = truncateAfter("Lorem ipsum labore ad in consequat laboris in mollit fugiat et do laborum aliqua laborum mollit amet laborum duis irure irure ut aute pariatur pariatur duis dolore in sed nisi occaecat officia nisi et esse ut magna et.", 30, " ... ");
    
    let res3 = truncateMiddle("Lorem ipsum labore ad in consequat laboris in mollit fugiat et do laborum aliqua laborum mollit amet laborum duis irure irure ut aute pariatur pariatur duis dolore in sed nisi occaecat officia nisi et esse ut magna et.", 30, " ... ");
    
    console.log("truncateBefore: " + res1);
    console.log("truncateAfter: " + res2);
    console.log("truncateMiddle: " + res3);

    如果你想避免重复的单词,你可以从字符串中间找到第一个空白字符来分割字符串。

    function truncateAfter( str, length, delimiter ) {
        return str.substring( 0, ( str.indexOf( ' ', str.substring( length ) ) + length ) ) + delimiter;
    }
    
    /**
     * Return first and last N character of a string without
     * break any word.
    */
    function truncateMiddle(str, length, delimiter) {
      let middle = str.indexOf(' ', str.length / 2);
     
      return truncateAfter(str.substring(0, middle), length, '') 
              + delimiter 
              + truncateAfter(str.substring(middle).split(' ').reverse().join(' '), length, '').split(' ').reverse().join(' ');
    }
    
    
    let res = truncateMiddle("This is a test.", 110, " ... ");
    
    console.log("truncateMiddle: " + res);

    【讨论】:

      【解决方案3】:

      检查此来源是否按单词截断: Yii2 core StringHelper:truncateWords

      【讨论】:

      • Yii2 是 PHP 框架。问题是关于将原始、有效的 PHP 代码移植到 Javascript ;-)
      • @BrunoAugusto 首先更改您的 PHP 代码,您编写的逐字截断字符串的 PHP 代码并不有趣。
      • 1) 这是一个有 4 年历史的代码(或更多) 2) 这对 来说并不有趣,但它在当时起到了它的作用 3) 这是一个片段为了遵守 Stack Overflow 的指导方针,我提取了一个完整的东西,即有一个完整且可验证的示例,等等。在实际提取此代码的过程中,我将字符串作为对象进行了完整的原型化,类似于我们在 JS 中所拥有的
      猜你喜欢
      • 2019-01-03
      • 2011-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多