在N個部分中拆分html字符串

c# html-agility-pack htmltidy regex

有沒有人有一個分裂html字符串(來自一個小的mce編輯器)並使用C#將它分成N個部分的例子?

我需要均勻地分割字符串而不分割單詞。

我想只是拆分html並使用HtmlAgilityPack嘗試修復損壞的標籤。雖然我不確定如何找到分裂點,但理想情況下它應該基於文本上的purley而不是html。

誰有任何想法如何去做?

UPDATE

根據要求,這是輸入和所需輸出的示例。

INPUT:

<p><strong>Lorem ipsum dolor sit amet, <em>consectetur adipiscing</em></strong> elit.</p>

輸出(當分成3個小組時):

Part1: <p><strong>Lorem ipsum dolor</strong></p>
Part2: <p><strong>sit amet, <em>consectetur</em></strong></p>
Part3: <p><strong><em>adipiscing</em></strong> elit.</p>

更新2:

我剛剛玩過Tidy HTML,這似乎可以很好地修復損壞的標籤,所以如果我能找到一種方法來找到拆分品牌,這可能是個不錯的選擇?

更新3

在.NET C#中對整個單詞使用類似於此Truncate字符串的方法,我現在設法獲得將構成每個部分的純文本單詞列表。所以,比如說使用Tidy HTML我有一個有效的HTML結構,並給出了這個單詞列表,任何人都知道現在最好的分割方法是什麼?

更新4

任何人都可以看到使用正則表達式以下列方式使用HTML查找索引的問題:

給定純文本字符串“sit amet,consectetur”,用正則表達式替換所有空格“(\ s | <(。| \ n)+?>)*”,理論上找到包含空格和/或任何組合的字符串標籤

然後我可以使用Tidy HTML修復損壞的html標籤?

非常感謝

馬特

一般承認的答案

建議的解決方案

伙計,這是我的詛咒 !我顯然不能在沒有花費太多時間的情況下擺脫困境,並且包括不合理的時間。

我想到了這個。我想到了HTML Tidy,也許它會起作用,但我無法繞過它。

所以,我寫了自己的解決方案。

我在你的輸入和我自己拼在一起的其他輸入上測試了這個。它似乎工作得很好。肯定會有洞,但它可能會為你提供一個起點。

無論如何,我的方法是這樣的:

  1. 使用包含有關該單詞在HTML文檔層次結構中的位置的信息的類來封裝HTML文檔中單個單詞的概念,直到給定的“頂部”。我在下面的HtmlWord類中實現了這個。
  2. 創建一個能夠編寫由上述HTML單詞組成的單行的類,以便在適當的位置添加start-element和end-element標記。我在下面的HtmlLine類中實現了這個。
  3. 編寫一些擴展方法,以便直接從HtmlAgilityPack.HtmlNode對象直接訪問這些類。這些我已在下面的HtmlHelper類中實現。

這件事我瘋了嗎?可能是。但是,你知道,如果你無法弄清楚任何其他方式,你可以嘗試一下。

以下是它如何與您的示例輸入一起使用:

var document = new HtmlDocument();
document.LoadHtml("<p><strong>Lorem ipsum dolor sit amet, <em>consectetur adipiscing</em></strong> elit.</p>");

var nodeToSplit = document.DocumentNode.SelectSingleNode("p");
var lines = nodeToSplit.SplitIntoLines(3);

foreach (var line in lines)
    Console.WriteLine(line.ToString());

輸出:

<p><strong>Lorem ipsum dolor </strong></p>
<p><strong>sit amet, <em>consectetur </em></strong></p>
<p><strong><em>adipiscing </em></strong>elit. </p>

現在代碼:

HtmlWord類

using System;
using System.Collections.Generic;
using System.Linq;

using HtmlAgilityPack;

public class HtmlWord {
    public string Text { get; private set; }
    public HtmlNode[] NodeStack { get; private set; }

    // convenience property to display list of ancestors cleanly
    // (for ease of debugging)
    public string NodeList {
        get { return string.Join(", ", NodeStack.Select(n => n.Name).ToArray()); }
    }

    internal HtmlWord(string text, HtmlNode node, HtmlNode top) {
        Text = text;
        NodeStack = GetNodeStack(node, top);
    }

    private static HtmlNode[] GetNodeStack(HtmlNode node, HtmlNode top) {
        var nodes = new Stack<HtmlNode>();

        while (node != null && !node.Equals(top)) {
            nodes.Push(node);
            node = node.ParentNode;
        };

        return nodes.ToArray();
    }
}

HtmlLine類

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;

using HtmlAgilityPack;

[Flags()]
public enum NodeChange {
    None = 0,
    Dropped = 1,
    Added = 2
}

public class HtmlLine {
    private List<HtmlWord> _words;
    public IList<HtmlWord> Words {
        get { return _words.AsReadOnly(); }
    }

    public int WordCount {
        get { return _words.Count; }
    }

    public HtmlLine(IEnumerable<HtmlWord> words) {
        _words = new List<HtmlWord>(words);
    }

    private static NodeChange CompareNodeStacks(HtmlWord x, HtmlWord y, out HtmlNode[] droppedNodes, out HtmlNode[] addedNodes) {
        var droppedList = new List<HtmlNode>();
        var addedList = new List<HtmlNode>();

        // traverse x's NodeStack backwards to see which nodes
        // do not include y (and are therefore "finished")
        foreach (var node in x.NodeStack.Reverse()) {
            if (!Array.Exists(y.NodeStack, n => n.Equals(node)))
                droppedList.Add(node);
        }

        // traverse y's NodeStack forwards to see which nodes
        // do not include x (and are therefore "new")
        foreach (var node in y.NodeStack) {
            if (!Array.Exists(x.NodeStack, n => n.Equals(node)))
                addedList.Add(node);
        }

        droppedNodes = droppedList.ToArray();
        addedNodes = addedList.ToArray();

        NodeChange change = NodeChange.None;
        if (droppedNodes.Length > 0)
            change &= NodeChange.Dropped;
        if (addedNodes.Length > 0)
            change &= NodeChange.Added;

        // could maybe use this in some later revision?
        // not worth the effort right now...
        return change;
    }

    public override string ToString() {
        if (WordCount < 1)
            return string.Empty;

        var lineBuilder = new StringBuilder();

        using (var lineWriter = new StringWriter(lineBuilder))
        using (var xmlWriter = new XmlTextWriter(lineWriter)) {
            var firstWord = _words[0];
            foreach (var node in firstWord.NodeStack) {
                xmlWriter.WriteStartElement(node.Name);
                foreach (var attr in node.Attributes)
                    xmlWriter.WriteAttributeString(attr.Name, attr.Value);
            }
            xmlWriter.WriteString(firstWord.Text + " ");

            for (int i = 1; i < WordCount; ++i) {
                var previousWord = _words[i - 1];
                var word = _words[i];

                HtmlNode[] droppedNodes;
                HtmlNode[] addedNodes;

                CompareNodeStacks(
                    previousWord,
                    word,
                    out droppedNodes,
                    out addedNodes
                );

                foreach (var dropped in droppedNodes)
                    xmlWriter.WriteEndElement();
                foreach (var added in addedNodes) {
                    xmlWriter.WriteStartElement(added.Name);
                    foreach (var attr in added.Attributes)
                        xmlWriter.WriteAttributeString(attr.Name, attr.Value);
                }

                xmlWriter.WriteString(word.Text + " ");

                if (i == _words.Count - 1) {
                    foreach (var node in word.NodeStack)
                        xmlWriter.WriteEndElement();
                }
            }
        }

        return lineBuilder.ToString();
    }
}

HtmlHelper靜態類

using System;
using System.Collections.Generic;
using System.Linq;

using HtmlAgilityPack;

public static class HtmlHelper {
    public static IList<HtmlLine> SplitIntoLines(this HtmlNode node, int wordsPerLine) {
        var lines = new List<HtmlLine>();

        var words = node.GetWords(node.ParentNode);

        for (int i = 0; i < words.Count; i += wordsPerLine) {
            lines.Add(new HtmlLine(words.Skip(i).Take(wordsPerLine)));
        }

        return lines.AsReadOnly();
    }

    public static IList<HtmlWord> GetWords(this HtmlNode node, HtmlNode top) {
        var words = new List<HtmlWord>();

        if (node.HasChildNodes) {
            foreach (var child in node.ChildNodes)
                words.AddRange(child.GetWords(top));
        } else {
            var textNode = node as HtmlTextNode;
            if (textNode != null && !string.IsNullOrEmpty(textNode.Text)) {
                string[] singleWords = textNode.Text.Split(
                    new string[] {" "},
                    StringSplitOptions.RemoveEmptyEntries
                );
                words.AddRange(
                    singleWords
                        .Select(w => new HtmlWord(w, node.ParentNode, top)
                    )
                );
            }
        }

        return words.AsReadOnly();
    }
}

結論

重申一下:這是一個綜合解決方案;我確定它有問題。我只是把它作為你要考慮的起點 - 再次,如果你無法通過其他方式得到你想要的行為。


熱門答案

這個建議只是一個黑客 - 希望有更好的方法。

基本上,您希望獲取一大塊HTML格式的文本並將其拆分為較小的部分,這些部分仍保留原始字體等。我認為您可以將原始HTML加載到RTF控件或Word對像中,將其拆分為保留格式的片段,然後將這些片段作為單獨的HTML輸出。

如果它提供了一種使用原始HTML格式化信息提取文本的簡單方法,也可能有這樣的方法使用HtmlAgilityPack。



許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因