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

有没有人有一个分裂html字符串(来自一个小的mce编辑器)并使用C#将它分成N个部分的例子?

我需要均匀地分割字符串而不分割单词。

我想只是拆分html并使用HtmlAgilityPack尝试修复损坏的标签。 虽然我不确定如何找到分裂点,但理想情况下它应该基于文本上的purley而不是html。

谁有任何想法如何去做?

UPDATE

根据要求,这是输入和所需输出的示例。

INPUT:

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

输出(当分成3个小组时):

 Part1: 

Lorem ipsum dolor

Part2:

sit amet, consectetur

Part3:

adipiscing elit.

更新2:

我刚刚玩过Tidy HTML,这似乎可以很好地修复损坏的标签,所以如果我能找到一种方法来找到拆分品牌,这可能是个不错的选择?

更新3

在.NET C#中对整个单词使用类似于此Truncate字符串的方法,我现在设法获得将构成每个部分的纯文本单词列表。 所以,比如说使用Tidy HTML我有一个有效的HTML结构,并给出了这个单词列表,任何人都知道现在最好的分割方法是什么?

更新4

任何人都可以看到使用正则表达式以下列方式使用HTML查找索引的问题:

给定纯文本字符串“sit amet,consectetur”,用正则表达式替换所有空格“(\ s | )*”,理论上找到包含空格和/或任何组合的字符串标签

然后我可以使用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("

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

"); var nodeToSplit = document.DocumentNode.SelectSingleNode("p"); var lines = nodeToSplit.SplitIntoLines(3); foreach (var line in lines) Console.WriteLine(line.ToString());

输出:

 

Lorem ipsum dolor

sit amet, consectetur

adipiscing elit.

现在代码:

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(); 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 _words; public IList Words { get { return _words.AsReadOnly(); } } public int WordCount { get { return _words.Count; } } public HtmlLine(IEnumerable words) { _words = new List(words); } private static NodeChange CompareNodeStacks(HtmlWord x, HtmlWord y, out HtmlNode[] droppedNodes, out HtmlNode[] addedNodes) { var droppedList = new List(); var addedList = new List(); // 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 SplitIntoLines(this HtmlNode node, int wordsPerLine) { var lines = new List(); 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 GetWords(this HtmlNode node, HtmlNode top) { var words = new List(); 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。