首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >截断文本块末尾的HTML内容(块元素)

截断文本块末尾的HTML内容(块元素)
EN

Stack Overflow用户
提问于 2015-06-18 22:10:46
回答 3查看 1.5K关注 0票数 10

当我们缩短/截断文本内容时,我们通常只是在特定的字符索引处截断它。这在HTML中已经很复杂了,但是我想使用不同的度量来截断我的HTML内容(使用内容可编辑的div生成):

  1. 我将定义字符索引N,它将用作截断起始点限制
  2. 算法将检查内容是否至少是N字符长(仅文本;不计数标记);如果不是,则只返回整个内容。
  3. 然后它将检查从N-XN+X字符位置(仅文本)并搜索块节点的末端;X是预定义的偏移量值,很可能是关于N/5N/4的;
  4. 如果几个块节点在此范围内结束,算法将选择最接近于限制索引N的节点。
  5. 如果在此范围内没有块节点结束,那么它将在同一范围内找到最接近的单词边界,并选择最接近N的索引并在该位置截断。
  6. 使用有效的HTML返回截断的内容(在末尾关闭的所有标记)

我的内容可编辑生成的内容可能包括段落(带换行)、预先格式化的代码块、块引号、有序和无序列表、标题、粗体和斜体(它们是内联节点,不应该在截断过程中计算)等等。当然,最后的实现将定义哪些元素是可能的截断候选项。头,即使它们是块HTML元素,也不会被计算为截断点,因为我们不希望被守恒的标题。段落,列出个别项目,完整的有序和无序列表,块引号,预先格式化的块,空元素等都是好的。标题和所有内联块元素都不是。

示例

让我们把这个非常堆栈溢出的问题作为HTML内容的一个例子,我想截断它。让我们将截断限制设置为1000,偏移量为250个字符(1/4)。

这个DotNetFiddle显示了这个问题的文本,同时在其中添加了限制标记(|MIN|表示字符750,|LIMIT|表示字符1000,|MAX|表示字符1250)。

从示例中可以看出,两个块节点之间最接近的截断边界(到字符1000 )是在</OL>P (我的内容可编辑生成.)之间。这意味着我的HTML应该在这两个标记之间被截断,这将导致稍微小于1000个字符的长内容文本,但是保持截断内容的意义,因为它不会只是在某个文本段落中间的某个地方截断。

我希望这能解释为什么事情应该与这个算法相关。

问题所在

我在这里看到的第一个问题是,我正在处理像HTML这样的嵌套结构。我还必须检测不同的元素(只有块元素,没有内联元素)。最后但并非最不重要的一点是,我只需计算字符串中的某些字符,而忽略那些属于标记的字符。

可能的解决办法

  1. 我可以通过创建表示内容节点及其层次结构的对象树来手动解析我的内容。
  2. 我可以将HTML转换成更容易管理的东西,比如标记,然后简单地搜索与我提供的索引N最近的新行,然后再转换回HTML。
  3. 使用类似的内容,用它替换我的#1解析,然后以某种方式使用XPath提取块节点并截断内容

二思

  • 我相信我能做到第一,但它觉得我正在重新发明车轮。
  • 我认为#2没有任何C#库,所以我应该手动执行,或者作为外部进程运行pandoc。
  • 我可以使用HAP,因为它很擅长操作HTML,但我不确定通过使用它我的截断是否足够简单。恐怕在我的自定义代码中,大部分处理仍在HAP之外

应该如何处理这种截断算法?我的头脑似乎太累了,无法达成共识(或解决方案)。

EN

回答 3

Stack Overflow用户

发布于 2015-06-30 09:57:55

下面是一些可以截断内部文本的示例代码。它使用了InnerText属性和CloneNode方法的递归功能。

代码语言:javascript
复制
    public static HtmlNode TruncateInnerText(HtmlNode node, int length)
    {
        if (node == null)
            throw new ArgumentNullException("node");

        // nothing to do?
        if (node.InnerText.Length < length)
            return node;

        HtmlNode clone = node.CloneNode(false);
        TruncateInnerText(node, clone, clone, length);
        return clone;
    }

    private static void TruncateInnerText(HtmlNode source, HtmlNode root, HtmlNode current, int length)
    {
        HtmlNode childClone;
        foreach (HtmlNode child in source.ChildNodes)
        {
            // is expected size is ok?
            int expectedSize = child.InnerText.Length + root.InnerText.Length;
            if (expectedSize <= length)
            {
                // yes, just clone the whole hierarchy
                childClone = child.CloneNode(true);
                current.ChildNodes.Add(childClone);
                continue;
            }

            // is it a text node? then crop it
            HtmlTextNode text = child as HtmlTextNode;
            if (text != null)
            {
                int remove = expectedSize - length;
                childClone = root.OwnerDocument.CreateTextNode(text.InnerText.Substring(0, text.InnerText.Length - remove));
                current.ChildNodes.Add(childClone);
                return;
            }

            // it's not a text node, shallow clone and dive in
            childClone = child.CloneNode(false);
            current.ChildNodes.Add(childClone);
            TruncateInnerText(child, root, childClone, length);
        }
    }

以及一个示例C#控制台应用程序,它将把这个问题作为一个例子,并将其截断为500个字符。

代码语言:javascript
复制
  class Program
  {
      static void Main(string[] args)
      {
          var web = new HtmlWeb();
          var doc = web.Load("http://stackoverflow.com/questions/30926684/truncating-html-content-at-the-end-of-text-blocks-block-elements");
          var post = doc.DocumentNode.SelectSingleNode("//td[@class='postcell']//div[@class='post-text']");
          var truncated = TruncateInnerText(post, 500);
          Console.WriteLine(truncated.OuterHtml);
          Console.WriteLine("Size: " + truncated.InnerText.Length);
      }
  }

运行它时,它应该显示如下:

代码语言:javascript
复制
<div class="post-text" itemprop="text">

<p>Mainly when we shorten/truncate textual content we usually just truncate it at specific character index. That's already complicated in HTML anyway, but I want to truncate my HTML content (generated using content-editable <code>div</code>) using different measures:</p>

<ol>
<li>I would define character index <code>N</code> that will serve as truncating startpoint <em>limit</em></li>
<li>Algorithm will check whether content is at least <code>N</code> characters long (text only; not counting tags); if it's not it will just return the whole content</li>
<li>It would then</li></ol></div>
Size: 500

注意:我没有在字界截断,只是在字符边界处截断,而且不,我的评论中的建议一点也不符合:-)

票数 3
EN

Stack Overflow用户

发布于 2015-06-29 17:45:43

代码语言:javascript
复制
   private void RemoveEmpty(HtmlNode node){
       var parent = node.Parent;
       node.Remove();
       if(parent==null)
           return;
       // remove parent if it is empty
       if(!parent.DescendantNodes.Any()){
           RemoveEmpty(parent);
       }
   }



private void Truncate(DocumentNode root, int maxLimit){

    var n = 0;
    HtmlTextNode lastNode = null;

    foreach(var node in root.DescendantNodes
         .OfType<HtmlTextNode>().ToArray()){
       var length = node.Text.Length;

       n+= length;
       if(n + length >= maxLimit){
            RemoveEmpty(node);
       }

    }
}

// you are left with only nodes that add up to your max limit characters.
票数 0
EN

Stack Overflow用户

发布于 2015-06-20 08:18:09

我将运行整个DOM树,并继续计算出现的文本字符数。每当我达到极限(N),我将删除该文本节点的额外字符,从那里我将只删除所有的文本节点。

我相信这是一个安全的方法,以保持所有的HTML+CSS结构,而只保留N个字符。

票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30926684

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档