У кого-либо есть пример разделения строки HTML (прибывающий от крошечного mce редактора) и разделяющий его на части N с помощью C#?
Я должен разделить строку равномерно, не разделяя слова.
Я думал о просто разделении HTML и использовании HtmlAgilityPack, чтобы попытаться зафиксировать поврежденные теги. Хотя я не уверен, как найти точку разделения, поскольку Идеально она должна базироваться purley на тексте, а не HTML также.
Кто-либо надел какие-либо идеи, как пойти об этом?
ОБНОВЛЕНИЕ
Согласно просьбе вот пример входа и желаемого вывода.
ВХОД:
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
ВЫВОД (Когда разделено на 3 седла):
Part1: Lorem ipsum dolor
Part2: sit amet, consectetur
Part3: adipiscing elit.
ОБНОВЛЕНИЕ 2:
У меня только что была игра с Опрятным HTML, и это, кажется, работает хорошо при фиксации поврежденных тегов, таким образом, это может быть хорошим вариантом, если я могу найти способ определить местоположение пинт разделения?
ОБНОВЛЕНИЕ 3
Используя метод, подобный этой Усеченной строке на целых словах в.NET C#, мне теперь удалось получить список слов простого текста, которые составят каждую часть. Так, скажите, что использование Приводит в порядок HTML, у меня есть допустимая структура XML для HTML, и, учитывая этот список слов, кто-либо понял то, каков теперь будет лучший способ разделить его?
ОБНОВЛЕНИЕ 4
Может кто-либо видеть проблему с использованием regex для нахождения индексов с HTML следующим способом:
Учитывая строку обычного текста "находятся сережка, consectetur", заменяют все пробелы regex" (\s | <(. |\n) +?>) * ", в теории, находящей, что строка с любой комбинацией пробелов и/или тегов
Я мог затем просто использовать Опрятный HTML для фиксации поврежденных тегов HTML?
Большое спасибо
Матовый
Блин, это мое проклятие! Я, видимо, не могу уйти от проблемы, не потратив на нее неразумное количество времени.
Я думал об этом. Я думал о HTML Tidy, и, возможно, это сработало бы, но мне было трудно уложить это в голове.
Поэтому я написал свое собственное решение.
Я протестировал его на вашем вводе и на некоторых других вводах, которые я сам создал. Кажется, оно работает довольно хорошо. Конечно, в нем есть дыры, но это может дать вам отправную точку.
В любом случае, мой подход был следующим:
HtmlWord
, приведенном ниже. HtmlLine
ниже. 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>
А теперь код:
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();
}
}
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();
}
}
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-код.
Также может быть способ использования HtmlAgilityPack, подобный этому, если он обеспечивает простой способ извлечения текста с информацией о форматировании из исходного HTML.