HtmlAgilityPack sous-chaîne de tous par longueur

c# html-agility-pack

Question

J'ai du HTML avec des éléments imbriqués (la plupart du temps juste des éléments div et p ). Évidemment, le nombre de lettres ne doit pas énumérer les balises HTML, mais seulement compter les lettres InnerText de chaque élément HTML. Le résultat HTML doit conserver la structure appropriée - toutes les balises de fermeture afin de rester valide.

Exemple de saisie:

<div>
    <p>some text</p>
    <p>some more text some more text some more text some more text some more text</p>
    <div>
        <p>some more text some more text some more text some more text some more text</p>
        <p>some more text some more text some more text some more text some more text</p>
    </div>
</div>

Étant donné int length = 16 le résultat devrait ressembler à ceci:

<div>
    <p>some text</p>
    <p>some more text some more text some more text some more text some more text</p>
    <div>
        <p>some more text some more text some more text some more text some more text</p>
        <p>some more text some more text some more text some more text some more text</p>
    </div>
</div>

Notez que le nombre de lettres (espaces compris) est 16. Le nombre <div> est éliminé car le nombre de lettres a atteint une length variable. Notez que la sortie HTML est toujours valide.

J'ai essayé ce qui suit, mais cela ne fonctionne pas vraiment. La sortie n'est pas comme prévu: certains éléments HTML sont répétés.

<div>
    <p>some text</p>
    <p>some more text some more text some more text some more text some more text</p>
    <div>
        <p>some more text some more text some more text some more text some more text</p>
        <p>some more text some more text some more text some more text some more text</p>
    </div>
</div>

METTRE À JOUR

@SergeBelov a fourni une solution qui fonctionne pour le premier exemple d'entrée. Toutefois, des tests supplémentaires ont présenté un problème avec une entrée comme celle ci-dessous.

Exemple de saisie n ° 2:

<div>
    <p>some text</p>
    <p>some more text some more text some more text some more text some more text</p>
    <div>
        <p>some more text some more text some more text some more text some more text</p>
        <p>some more text some more text some more text some more text some more text</p>
    </div>
</div>

Étant donné que la variable int maxLength = 7; une sortie doit être égale à quelques mois . Cela ne fonctionne pas comme ça à cause de ce code où ParentNode = null :

<div>
    <p>some text</p>
    <p>some more text some more text some more text some more text some more text</p>
    <div>
        <p>some more text some more text some more text some more text some more text</p>
        <p>some more text some more text some more text some more text some more text</p>
    </div>
</div>

La création d'un nouveau HtmlNode ne semble pas aider car sa propriété InnterText est en lecture seule.

Réponse acceptée

Le petit programme de console ci-dessous illustre une approche possible:

  1. Sélectionnez les nœuds de texte pertinents et calculez leur longueur totale.
  2. Prenez autant de nœuds que nécessaire pour atteindre le total cumulé au-delà de la longueur maximale;
  3. Supprimez tous les nœuds d'élément du document, à l'exception de ceux qui sont les ancêtres des nœuds sélectionnés lors des étapes ## 1, 2;
  4. Coupez le texte dans le dernier nœud de la liste pour l'adapter à la longueur maximale.

UPDATE: Ceci devrait toujours fonctionner avec un noeud de texte étant le premier; probablement, un Trim() est requis pour supprimer les espaces comme indiqué ci-dessous.

    static void Main(string[] args)
    {
        int maxLength = 9;
        string input = @"
            some more text some more text 
            <div>
                <p>some text</p>
                <p>some more text some more text some more text some more text some more text</
            </div>";

        var doc = new HtmlDocument();
        doc.LoadHtml(input);

        // Get text nodes with the appropriate running total
        var acc = 0;
        var nodes = doc.DocumentNode
            .Descendants()
            .Where(n => n.NodeType == HtmlNodeType.Text && n.InnerText.Trim().Length > 0)
            .Select(n => 
            {
                var length = n.InnerText.Trim().Length;
                acc += length;
                return new { Node = n, TotalLength = acc, NodeLength = length }; 
            })
            .TakeWhile(n => (n.TotalLength - n.NodeLength) < maxLength)
            .ToList();

        // Select element nodes we intend to keep
        var nodesToKeep = nodes
            .SelectMany(n => n.Node.AncestorsAndSelf()
                .Where(m => m.NodeType == HtmlNodeType.Element));

        // Select and remove element nodes we don't need
        var nodesToDrop = doc.DocumentNode
            .Descendants()
            .Where(m => m.NodeType == HtmlNodeType.Element)
            .Except(nodesToKeep)
            .ToList();

        foreach (var r in nodesToDrop)
            r.Remove();

        // Shorten the last node as required
        var lastNode = nodes.Last();
        var lastNodeText = lastNode.Node;
        var text = lastNodeText.InnerText.Trim().Substring(0,
                lastNode.NodeLength - lastNode.TotalLength + maxLength);
        lastNodeText
            .ParentNode
            .ReplaceChild(HtmlNode.CreateNode(text), lastNodeText);

        doc.Save(Console.Out);
    }



Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi