获取HTML内容的前100个字符而不剥离标记

时间:2010-03-29 20:11:39

标签: c# asp.net html

关于如何剥离html标签有很多问题,但关于它们的函数/方法并不多。

这是情况。我有一个500字符的消息摘要(包括html标签),但我只想要前100个字符。问题是如果我截断消息,它可能在html标签的中间......这会弄乱一些东西。

假设html是这样的:

<div class="bd">"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. <br/>
 <br/>Some Dates: April 30 - May 2, 2010 <br/>
 <p>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. <em>Duis aute irure dolor in reprehenderit</em> in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <br/>
 </p>
 For more information about Lorem Ipsum doemdloe, visit: <br/>
 <a href="http://www.somesite.com" title="Some Conference">Some text link</a><br/> 
</div>

我如何拍摄前100个字符左右? (虽然理想情况下,这将是“CONTENT”的第一个大约100个字符(在html标签之间)

我假设最好的方法是使用递归算法来跟踪html标签并附加任何会被截断的标签,但这可能不是最好的方法。

我的第一个想法是使用递归来计算嵌套标签,当我们达到100个字符时,查找下一个“&lt;”然后使用递归来编写那里所需的结束html标记。

这样做的原因是对现有文章做一个简短的总结,而不要求用户返回并提供所有文章的摘要。如果可能的话,我想保留html格式。

注意:请忽略html不是完全语义的。这是我必须从我的WYSIWYG处理的事情。

编辑:

我添加了一个潜在的解决方案(似乎有效)我认为其他人也会遇到这个问题。我不确定它是最好的...而且它可能不是非常强大(事实上,我知道它不是),但我很感激任何反馈

7 个答案:

答案 0 :(得分:4)

这是大多数情况下的解决方案。它不处理不正确的html标签,以及诸如“a&lt; b&gt; c”的情况。但它适用于我的目的,也许对其他人有帮助。

    /// <summary>
    /// Gets first number of characters from the html string without stripping tags
    /// </summary>
    /// <param name="htmlString">The html string, not encoded, pure html</param>
    /// <param name="length">The number of first characters to get</param>
    /// <returns>The html string</returns>
    public static string GetFirstCharacters(string htmlString, int length)
    {
        if (htmlString == null)
            return string.Empty;

        if(htmlString.Length < length)
            return htmlString;

        // regex to separate string on parts: tags, texts
        var separateRegex = new Regex("([^>][^<>]*[^<])|[\\S]{1}");
        // regex to identify tags
        var tagsRegex = new Regex("^<[^>]+>$");

        // separate string on tags and texts
        var matches = separateRegex.Matches(htmlString);

        // looping by mathes
        // if it's a tag then just append it to resuls,
        // if it's a text then append substing of it (considering the number of characters)
        var counter = 0;
        var sb = new StringBuilder();
        for (var i = 0; i < matches.Count; i++)
        {
            var m = matches[i].Value;

            // check if it's a tag
            if (tagsRegex.IsMatch(m))
            {
                sb.Append(m);
            }
            else
            {
                var lengthToCut = length - counter;

                var sub = lengthToCut >= m.Length
                    ? m
                    : m.Substring(0, lengthToCut);

                counter += sub.Length;
                sb.Append(sub);
            }
        }

        return sb.ToString();
    }

答案 1 :(得分:3)

如果您将HTML解析为DOM结构,然后开始遍历广度优先或深度优先,无论您喜欢什么,收集节点文本直到达到100个字符?

答案 2 :(得分:1)

过去我用正则表达式完成了这个。抓取内容,通过正则表达式删除标签,然后将其修剪到所需的长度。

当然,这会删除所有HTML,这就是我想要的。如果你想保留HTML,我会考虑不关闭开放标签,而是删除开放标签。

答案 3 :(得分:1)

我的建议是找到一个HTML友好的遍历器(允许你像XML一样遍历HTML),然后从头开始标签忽略标签本身,只计算标签中的数据。计算到你的极限,然后一旦达到关闭每个标签(我不能想到任何不仅仅是标签的标签)。

这应该运作得相当好,并且与您正在寻找的相当接近。

它完全脱离了ol'noggin的顶部所以我假设会有一些棘手的部分,比如显示的属性值(例如链接标记值)。

答案 4 :(得分:1)

我决定推出自己的解决方案......只是为了应对挑战。

如果有人能看到任何逻辑错误或效率低下,请告诉我。

我不知道这是否是最好的方法......但似乎有效。有可能它不起作用的情况......如果html不正确,它可能会失败。

/// <summary>
/// Get the first n characters of some html text
/// </summary>
private string truncateTo(string s, int howMany, string ellipsis) {

    // return entire string if it's more than n characters
    if (s.Length < howMany)
        return s;

    Stack<string> elements = new Stack<string>();
    StringBuilder sb = new StringBuilder();
    int trueCount = 0;

    for (int i = 0; i < s.Length; i++) {
        if (s[i] == '<') {

            StringBuilder elem = new StringBuilder();
            bool selfclosing = false;

            if (s[i + 1] == '/') {

                elements.Pop(); // Take the previous element off the stack
                while (s[i] != '>') {
                    i++;
                }
            }
            else { // not a closing tag so get the element name

                while (i < s.Length && s[i] != '>') {

                    if ((s[i] >= 'a' && s[i] <= 'z') || (s[i] >= 'A' && s[i] <= 'Z')) {
                        elem.Append(s[i]);
                    }
                    else if (s[i] == '/' || s[i] == ' ') {

                        // self closing tag or end of tag name. Find the end of tag
                        do {
                            if (s[i] == '/' && s[i + 1] == '>') {
                                // at the end of self-closing tag. Don't store
                                selfclosing = true;
                            }

                            i++;
                        } while (i < s.Length && s[i] != '>');
                    }
                    i++;
                } // end while( != '>' )

                if (!selfclosing)
                    elements.Push(elem.ToString());
            } 
        }
        else {
            trueCount++;
            if (trueCount > howMany) {
                sb.Append(s.Substring(0, i - 1));
                sb.Append(ellipsis);
                while (elements.Count > 0) {
                    sb.AppendFormat("</{0}>", elements.Pop());
                }
            }
        }
    }

    return sb.ToString();
}

答案 5 :(得分:0)

我使用了XmlReader和XmlWriter来执行此操作: https://gist.github.com/2413598

正如其他人所说,您应该使用SgmlReader或HtmlAgilityPack来对传入的字符串进行处理。

答案 6 :(得分:0)

我明白了你的问题。在do while循环中有一个错误:

 } while (i < s.Length && s[i] != '>');

应替换为

 } while (i < s.Length && ***s[i+1]*** != '>');