Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/320.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 截断文本块(块元素)末尾的HTML内容_C#_Html_Extract_Truncate - Fatal编程技术网

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

C# 截断文本块(块元素)末尾的HTML内容,c#,html,extract,truncate,C#,Html,Extract,Truncate,主要是当我们缩短/截断文本内容时,我们通常只在特定的字符索引处截断它。这在HTML中已经很复杂了,但我想用不同的方法截断我的HTML内容(使用content editablediv生成): 我将定义字符索引N,它将用作截断起始点限制 算法将检查内容是否至少有N个字符长(仅限文本;不包括标签);如果不是,它将返回全部内容 然后,它将检查从N-X到N+X字符位置(仅文本),并搜索块节点的末端X是预定义的偏移量值,可能约为N/5到N/4 如果多个块节点在该范围内结束,算法将选择最接近极限索引的一个 如

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

  • 我将定义字符索引
    N
    ,它将用作截断起始点限制
  • 算法将检查内容是否至少有
    N
    个字符长(仅限文本;不包括标签);如果不是,它将返回全部内容
  • 然后,它将检查从
    N-X
    N+X
    字符位置(仅文本),并搜索块节点的末端
    X
    是预定义的偏移量值,可能约为
    N/5
    N/4
  • 如果多个块节点在该范围内结束,算法将选择最接近极限索引的一个
  • 如果没有块节点在该范围内结束,它将在同一范围内找到最近的单词边界,并选择最靠近
    N
    的索引并在该位置截断
  • 返回带有有效HTML的截断内容(所有标记在结尾处关闭)
  • “我的内容”可编辑生成的内容可能包括段落(带换行符)、预格式化代码块、块引号、有序和无序列表、标题、粗体和斜体(它们是内联节点,不应计入截断过程)最后的实现当然会定义哪些元素是可能的截断候选元素。标题即使是块HTML元素,也不会被视为截断点,因为我们不希望出现寡头。段落、列出单个项目、整个有序和无序列表、块引号、预格式化块、空白元素等都是好的。标题和所有内联块元素都不是

    例子 让我们以这个stackoverflow问题为例来说明我想要截断的HTML内容。让我们将截断限制设置为1000,偏移量为250个字符(1/4)

    显示此问题的文本,同时在其内部添加限制标记(
    | MIN |
    表示字符750,
    | limit |
    表示字符1000和
    | MAX |
    表示字符1250)

    从示例中可以看出,两个块节点到字符1000之间最近的截断边界介于
    p
    之间(生成了我的内容可编辑…)。这意味着我的HTML应该在这两个标签之间被截断,这会导致少于1000个字符的长文本内容,但保留截断的内容是有意义的,因为它不会截断某些文本段落中间的某个地方。

    我希望这能解释这个算法应该如何工作

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

    可能的解决方案
  • 我可以通过创建一些表示内容节点及其层次结构的对象树来手动解析我的内容
  • 我可以将HTML转换成更容易管理的东西,比如降价,然后简单地搜索与我提供的索引最近的新行
    N
    ,然后转换回HTML
  • 使用类似于HTML Agility Pack的东西,用它替换my#1解析,然后以某种方式使用XPath提取块节点并截断内容
  • 再三考虑
    • 我相信我可以通过做1来做到,但感觉我在重新发明轮子
    • 我不认为有任何C#库用于#2,所以我也应该使用HTML手动标记,或者作为外部进程运行,即pandoc
    • 我可以使用HAP,因为它非常擅长处理HTML,但我不确定使用它是否会使截断变得足够简单。恐怕我的自定义代码中的大部分处理仍将在HAP之外

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

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

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

    private void removempty(HtmlNode节点){
    
       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.
    
      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);
          }
      }
    
    var parent=node.parent; node.Remove(); 如果(父项==null) 返回; //如果父项为空,则将其删除 如果(!parent.degenantNodes.Any()){ removempty(父母); } } 私有void截断(DocumentNode根,int maxLimit){ var n=0; HtmlTextNode lastNode=null; foreach(root.degenantNodes中的var节点 .OfType().ToArray()){ var length=node.Text.length; n+=长度; 如果(n+长度>=maxLimit){ removempty(节点); } } } //只剩下加起来达到最大限制字符数的节点。
    以下是一些可以截断内部文本的示例代码。它使用
    InnerText
    属性和
    CloneNode
    方法的递归功能

        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);
            }
        }
    
    运行时,应显示以下内容:

    <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
    

    注意:我没有在单词边界处截断,只是在字符边界处截断,不,它根本没有按照我的评论中的建议进行:-)

    但这只是通常的N个字符截断。。。不是我试图实现的每个块。这方面没有灵丹妙药,但我会使用HAP,HAP可以用一个xpath获取所有文本:
    //text()
    。然后,每个节点也有一个