【问题标题】:How do I break up a string without creating malformed HTML tags?如何在不创建格式错误的 HTML 标签的情况下拆分字符串?
【发布时间】:2015-06-08 00:21:16
【问题描述】:

我在做什么

  • 在 NodeJS 中,我正在使用 MustacheJS 创建一个电子邮件模板,并使用来自 JSON 对象数组的数据。
  • 模板中的文本/消息可以包含文本以及基本的 html 标记(例如 b pa)。
  • 由于篇幅限制,我只需要显示消息的摘录。为此,我进行了字数统计,然后说 20 个字(用空格检查),我截断字符串并附加 View more 锚标记。这会将其链接到包含完整帖子的网站的帖子页面。比如:

嘿,这是一个示例帖子文本<b>message</b>。 Lorem ipsum dolor 坐 阿米特... <a href="someurl">查看更多</a>

问题:

在字数统计和截断期间,我可能会截断 html 标记之间的字符串,因为我只是根据空格计算单词。比如:

我正在与您分享一个链接。 <a style="color:...<a href="someurl">查看更多</a>

现在这会破坏 html。

可能的解决方案:

  • 在截断字符串之前,对其运行正则表达式以查找其中的所有 html 标记。
  • 使用indexOf()(或其他方法)查找每个标签的开始和结束索引。
  • 字数统计后,获取我需要截断的索引。
  • 现在看看索引是否与任何标签区域相交。
  • 如果确实相交,只需将截断索引移动到 html 标记的开头或结尾即可。

问题:

有没有更好的方法来做到这一点。我不知道我应该在 google 上搜索哪些搜索词以获得帮助。

附:代码很灵活,如果有更好的解决方案,我可以更改流程。另外,我不擅长帖子标题。如果可以,请将其修改为反映问题的内容。


编辑:

这是我在亚历克斯回答后想出的。希望它可以帮助别人:

/**
 * Counter: Takes a string and returns words and characters count
 * @param value
 * @returns obj: {
 *      'wordCount': (int),
 *      'totalChars': (int),
 *      'charCount': (int),
 *      'charCountNoSpace': (int)
 *  }
 */
var counter = function(value){
    var regex = /\s+/gi;
    if (!value.length) {
        return {
            wordCount: 0,
            totalChars: 0,
            charCount: 0,
            charCountNoSpace: 0
        };
    }
    else {
        return {
            wordCount: value.trim().replace(regex, ' ').split(' ').length,
            totalChars: value.length,
            charCount: value.trim().length,
            charCountNoSpace: value.replace(regex, '').length
        };
    }
}


/**
 * htmlSubString - Creates excerpt from markup(or even plain text) without creating malformed HTML tags
 * @param markup {string} - Markup/text to take excerpt out of
 * @param limit {int} - Total word count of excerpt. Note that only text (not the html tag) counts as a valid word.
 * @returns {string} - Excerpt
 */
var htmlSubString = function(markup, limit){
    var htmlParser = require("htmlparser2");
    var tagCount = 0;
    var wordCount = 0;
    var excerpt = '';

    function addToExcerpt(type, text, attribs) {
        if ((wordCount >= limit && tagCount == 0) || (tagCount === 1 && type === 'tagOpen' && wordCount >= limit)) {
            return false;
        }
        else if (wordCount < limit || tagCount) {
            if (type === 'text') {
                var wordCountSubString = $scope.counter(text).wordCount;
                if (wordCountSubString + wordCount > limit && tagCount === 0) {
                    var length = limit - wordCount;
                    var wordList = text.trim().split(' ');

                    for (var i = 0; i < length; i++) {
                        excerpt += ' ' + wordList[i];
                        wordCount++;
                    }
                } else {
                    wordCount += wordCountSubString;
                    excerpt += text;
                }

            } else if (type === 'tagOpen') {
                excerpt += '<' + text;
                for (var prop in attribs) {
                    excerpt += ' ' + prop + '="' + attribs[prop] + '"';
                }
                excerpt += '>';
            } else if (type === 'tagClose') {
                excerpt += '</' + text + '>';
            }
        }

        return true;
    }

    var parser = new htmlParser.Parser({
        onopentag: function (name, attribs) {
            if(wordCount < limit){
                ++tagCount;
                addToExcerpt('tagOpen', name, attribs);
            }
        },
        ontext: function (text) {
            if(wordCount < limit){
                addToExcerpt('text', text);
            }
        },
        onclosetag: function (tagName) {
            if(wordCount < limit || tagCount > 0){
                addToExcerpt('tagClose', tagName);
                --tagCount;
            }
        }
    });

    parser.write(markup);
    parser.end();

    return excerpt;
}

用法:

var wordCountLimit = 20;
var markup = "/* some markup/text */";
var excerpt = htmlSubString(markup, wordCountLimit);

【问题讨论】:

    标签: javascript html node.js parsing html-parsing


    【解决方案1】:

    现在,您肯定能够找到一些与正则表达式匹配的 HTML 标记。也就是说,我不推荐它。一开始你会很开心,一切都会很好。然后明天你会发现一个小的边缘案例。 “不用担心!”您会说,当您修改表达式以解释差异时。然后第二天,一个新的调整,一个新的,又一个,等等,直到你不能再忍受了。

    我强烈建议您找到一个已经建立的 HTML 解析库。 npm 上似乎有很多。 This one 似乎很受欢迎。

    PS - 你的问题做得很好。我希望更多问题花费尽可能多的时间并提供尽可能多的细节:)

    【讨论】:

    • 查看链接。
    • 我想我们在这里可能会有赢家。虽然这里的成本比正则表达式解决方案要多,但这种方法不太容易出错。我将做一些基准测试,看看它对我的服务器的影响有多大,因为它需要及时发送大量电子邮件。
    • 我认为“成本”实际上对您来说更适合使用正则表达式。我一直在走这条路,相信我;)。此外,我几乎可以保证一个正确编写的解析库将比正则表达式具有更高的性能。我链接你的那个库甚至支持解析流:D
    • 没问题。我很高兴:)
    猜你喜欢
    • 2020-04-09
    • 2023-03-15
    • 1970-01-01
    • 1970-01-01
    • 2019-01-21
    • 2011-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多