Разделите строку HTML в частях N

У кого-либо есть пример разделения строки 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?

Большое спасибо

Матовый

6
задан Community 23 May 2017 в 12:00
поделиться

2 ответа

Предлагаемое решение

Блин, это мое проклятие! Я, видимо, не могу уйти от проблемы, не потратив на нее неразумное количество времени.

Я думал об этом. Я думал о 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();
    }
}

Заключение

Просто повторю: это готовое решение; я уверен, что у него есть проблемы. Я представляю его только как отправную точку для рассмотрения - опять же, если вы не можете добиться желаемого поведения другими способами.

17
ответ дан 8 December 2019 в 13:45
поделиться

Это предложение - всего лишь взлом - надеюсь, есть способ получше.

По сути, вы хотите взять кусок текста в формате HTML и разделить его на более мелкие части, которые по-прежнему сохраняют шрифт и т. Д. Оригинала. Я думаю, вы могли бы загрузить исходный HTML-код в элемент управления RTF или объект Word, разделить его на части, сохраняющие форматирование, а затем вывести эти части как отдельный HTML-код.

Также может быть способ использования HtmlAgilityPack, подобный этому, если он обеспечивает простой способ извлечения текста с информацией о форматировании из исходного HTML.

0
ответ дан 8 December 2019 в 13:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: