Как преобразовать HTML в обычный текст?

У меня есть фрагменты HTML, хранящиеся в таблице. Не целые страницы, без тегов и т.п., а только базовое форматирование.

Я хотел бы иметь возможность отображать этот Html только как текст, без форматирования, на данной странице (на самом деле только первые 30-50 символов, но это простой бит).

Как мне поместить «текст» в этот HTML-код в строку как обычный текст?

Итак, этот фрагмент кода.

<b>Hello World.</b><br/><p><i>Is there anyone out there?</i><p>

Становится:

Привет мир. Есть там кто-нибудь?

Вот несколько хороших предложений от W3C: w3.org/Tools/html2things.html

Rich 21.06.2012 19:09

Есть довольно простой и понятный код для преобразования HTML в обычный текст в blackbeltcoder.com/Articles/strings/convert-html-to-text.

Jonathan Wood 11.04.2011 19:30

Это был правильный ответ на то, что мне было нужно - спасибо!

Shaul Behr 29.03.2012 00:32

Вы можете использовать SgmlReader. code.msdn.microsoft.com/SgmlReader

Leonardo Herrera 13.11.2008 15:40

Как вопрос может быть отмечен как дубликат вопроса, заданного 6 месяцев спустя? Кажется немного отсталым ...

Stuart Helwig 26.06.2013 06:52

Я написал функция, которая преобразует HTML в обычный текст. У него есть некоторые ограничения, например, не извлекает ссылки из тегов a. Мне лучше основывать свою функцию на исходный код PHP html2text.

Uwe Keim 23.09.2018 17:14
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
109
6
218 767
19
Перейти к ответу Данный вопрос помечен как решенный

Ответы 19

общедоступная статическая строка StripTags2 (строка html) { return html.Replace ("<", "<"). Replace (">", ">"); }

Этим вы избегаете всех «<» и «>» в ​​строке. Это то, что вы хотите?

... ах. Что ж, теперь ответ (вместе с толкованием двусмысленного вопроса) полностью изменился, я выберу гниды из-за отсутствия & amp; кодирование вместо этого. ;-)

bobince 13.11.2008 15:50

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

Kramii 13.11.2008 18:28

Если у вас есть данные, содержащие теги HTML, и вы хотите отобразить их, чтобы человек мог ВИДЕТЬ теги, используйте HttpServerUtility :: HtmlEncode.

Если у вас есть данные, содержащие HTML-теги, и вы хотите, чтобы пользователь видел визуализированные теги, отобразите текст как есть. Если текст представляет собой всю веб-страницу, используйте для него IFRAME.

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

в php есть функция с названием striptags () возможно у вас есть что-то похожее

markus 14.11.2008 01:46

"использовать регулярное выражение" НЕТ! Это было бы занесением в черный список. Вы можете быть в безопасности только при занесении в белый список. Например, помните ли вы, что атрибут стиля может содержать "background: url ('javascript: ...');"? конечно нет, я бы тоже не стал. Вот почему черный список не работает.

usr 26.05.2011 22:34
stackoverflow.com/a/1732454/193892
Prof. Falken 19.02.2021 21:15
Ответ принят как подходящий

Если вы говорите об удалении тегов, это относительно просто, если вам не нужно беспокоиться о таких вещах, как теги <script>. Если все, что вам нужно сделать, это отобразить текст без тегов, вы можете сделать это с помощью регулярного выражения:

<[^>]*>

Если вам действительно нужно беспокоиться о тегах <script> и т.п., тогда вам понадобится что-то более мощное, чем регулярные выражения, потому что вам нужно отслеживать состояние, что-то большее, чем контекстно-свободная грамматика (CFG). Хотя вы могли бы сделать это с помощью «слева направо» или не жадного сопоставления.

Если вы умеете использовать регулярные выражения, существует множество веб-страниц с хорошей информацией:

Если вам нужно более сложное поведение CFG, я бы посоветовал использовать сторонний инструмент, к сожалению, я не знаю хорошего, что можно порекомендовать.

Вы также должны беспокоиться о> в значениях атрибутов, комментариях, PI / CDATA в XML и различных распространенных искажениях в устаревшем HTML. В общем, [X] [HT] ML не поддается синтаксическому анализу с помощью регулярных выражений.

bobince 13.11.2008 15:58

Вы можете разместить> в значениях атрибутов, но сделав атрибуты частью регулярного выражения. Только сложность вложенных тегов ограничивает полезность синтаксического анализа с помощью регулярных выражений.

vfilby 16.11.2008 18:33

Разве вы не имеете в виду <[^>] *>, который соответствует таким вещам, как <html>, а не <[^>]> *, который соответствует таким вещам, как <h> >>>?

Greg 30.06.2009 17:16

Это ужасный метод. Правильный способ - проанализировать HTML с помощью библиотеки и пройти по dom, выводя только содержимое из белого списка.

usr 26.05.2011 22:09

@usr: Вы имеете в виду часть ответа CFG. Regex можно использовать для быстрого и грязного удаления тегов, у него есть свои недостатки, но это быстро и легко. Для более сложного анализа используйте инструмент на основе CFG (на вашем языке библиотека, которая генерирует DOM). Я не проводил тесты, но держу пари, что синтаксический анализ DOM медленнее, чем удаление регулярных выражений, если необходимо учитывать производительность.

vfilby 30.05.2011 02:59

@vfilby: НЕТ! Удаление тегов занесено в черный список. Просто в качестве примера того, что вы забыли: ваше регулярное выражение не будет удалять теги, в которых отсутствует закрывающий '>'. Вы думали об этом? Я не уверен, может ли это быть проблемой, но это, по крайней мере, доказывает, что вы пропустили этот случай. Кто знает, что еще вы пропустили. Вот еще один: вы пропускаете изображения с атрибутом src javascript. НИКОГДА не заносите в черный список, за исключением случаев, когда безопасность не важна.

usr 31.05.2011 16:39

@usr: Вы имеете в виду часть ответа CFG. Regex можно использовать для быстрого и грязного удаления тегов, у него есть свои недостатки, но это быстро и легко. Для более сложного анализа используйте инструмент на основе CFG (на вашем языке библиотека, которая генерирует DOM).

vfilby 10.08.2011 01:26

@vfilby, первая атака, которая приходит на ум, - это написание "<div id = \" "(строковый синтаксис C#). Обратите внимание на отсутствующие конечные кавычки и отсутствующую закрывающую скобку. Я предполагаю, что это запутает браузер и разбалансирует структуру тегов. Вы думаете об этой атаке? Можете ли вы быть уверены, что она никогда не сработает?

usr 14.08.2011 23:38

@usr Неправильный ввод может так же легко запутать библиотеку синтаксического анализа (то, что я называю CFG), поэтому я не уверен, что это лучшее доказательство против удаления тегов. Вложенные теги не поддерживаются разделением тегов регулярных выражений, как указано в моем ответе (т.е. теги <script>). Итак, я повторяю свою первоначальную мысль: если задача проста, вы можете удалить теги (не занесение в черный или белый список, потому что вы удаляете их все). Если вам нужен более сложный синтаксический анализ (устойчивый к ошибкам, поэтапный и т. д.), Используйте CFG (синтаксическую библиотеку).

vfilby 21.06.2012 07:43

@vfilby, неважно, запуталась ли библиотека синтаксического анализа. Все, что вам нужно сделать, это взять из него DOM (любой DOM) и вывести только компоненты из белого списка. Это всегда безопасно, не имеет значения, как выглядит проанализированная DOM. Кроме того, я рассказал вам несколько примеров, когда ваш «простой» метод не удаляет теги.

usr 21.06.2012 17:34

Как писал @bobince, HTML не поддается синтаксическому анализу с помощью регулярных выражений. Это приведет к взрыву в реальном HTML, который часто искажен.

Judah Gabriel Himango 06.12.2012 02:38

не правильным образом, он только конвертирует теги, html-текст может содержать разрывы строк, табуляции и другое форматирование, которое это регулярное выражение не удаляет.

Amir Dora. 23.09.2020 21:27

Зависит от того, что вы подразумеваете под «HTML». Самый сложный случай - это целые веб-страницы. С этим также проще всего справиться, поскольку вы можете использовать веб-браузер в текстовом режиме. См. Список веб-браузеров Статья в Википедии, включая браузеры с текстовым режимом. Lynx, вероятно, самый известный, но один из других может быть лучше для ваших нужд.

как он сказал: «У меня есть фрагменты HTML, хранящиеся в таблице».

M at 22.06.2016 20:20

HTTPUtility.HTMLEncode() предназначен для обработки кодирования HTML-тегов как строк. Он берет на себя всю тяжелую работу за вас. Из Документация MSDN:

If characters such as blanks and punctuation are passed in an HTTP stream, they might be misinterpreted at the receiving end. HTML encoding converts characters that are not allowed in HTML into character-entity equivalents; HTML decoding reverses the encoding. For example, when embedded in a block of text, the characters < and >, are encoded as &lt; and &gt; for HTTP transmission.

Метод HTTPUtility.HTMLEncode(), подробный здесь:

public static void HtmlEncode(
  string s,
  TextWriter output
)

Использование:

String TestString = "This is a <Test String>.";
StringWriter writer = new StringWriter();
Server.HtmlEncode(TestString, writer);
String EncodedString = writer.ToString();

Действительно хороший ответ, Джордж, спасибо, он также подчеркнул, насколько плохо я задал вопрос в первый раз. Извиняюсь.

Stuart Helwig 14.11.2008 03:38

пакет гибкости html устарел и не поддерживает html5

abzarak 06.06.2015 18:23

Лицензированный MIT HtmlAgilityPack имеет в одном из своих образцов метод, который преобразует HTML в обычный текст.

var plainText = HtmlUtilities.ConvertToPlainText(string html);

Подайте ему строку HTML, например

<b>hello, <i>world!</i></b>

И вы получите результат в виде простого текста, например:

hello world!

Раньше я использовал HtmlAgilityPack, но я не вижу ссылки на ConvertToPlainText. Вы можете сказать мне, где я могу его найти?

horatio 08.01.2010 06:43

Горацио, он включен в один из примеров, поставляемых с HtmlAgilityPack: htmlagilitypack.codeplex.com/sourcecontrol/changeset/view/…

Judah Gabriel Himango 08.01.2010 18:37

Перед использованием этого метода я советую проверить, действительно ли он занесен в белый список изнутри. В противном случае это опасно.

usr 14.08.2011 23:39

На самом деле, в Agility Pack для этого нет встроенного метода. Вы связались с примером, в котором Agility Pack используется для обхода дерева узлов, удаления тегов script и style и записи внутреннего текста других элементов в строку вывода. Я сомневаюсь, что он прошел много испытаний с реальными данными.

Lou 02.09.2012 16:19

Да, это есть в образцах. Он использует базовую функциональность пакета гибкости HTML для анализа документа и вывода текста узлов, пропуская стили, сценарии и комментарии. Посмотрите код сами: htmlagilitypack.codeplex.com/sourcecontrol/changeset/view/…

Judah Gabriel Himango 06.12.2012 02:42

Вы заметите, что код помещает в черный список скриптовые блоки, комментарии и стили.

Judah Gabriel Himango 21.02.2013 00:15

Может ли кто-нибудь предоставить код, который работает, а не ссылки на образцы, которые необходимо модифицировать для правильной работы?

Eric K 17.09.2013 23:24

Вы смотрели ссылку, которую я разместил в комментариях? htmlagilitypack.codeplex.com/sourcecontrol/changeset/view/…

Judah Gabriel Himango 17.09.2013 23:50

Связанный образец отлично работает. Для тех, кто пытается его использовать, просто скопируйте весь класс в свой собственный проект и используйте метод ConvertHTML. Вам также потребуется загрузить в свой проект ссылку на dll HtmlAgilityPack.

rdans 16.10.2013 18:22

Все в порядке, но когда я пытаюсь преобразовать этот текст, я получаю предупреждение xss: <p> & lt; script & gt; alert ("Ups") & lt; / script & gt; </p>, потому что метод HtmlEntity.DeEntitize () convert & lt: script в <script>

Zabaa 26.02.2014 18:16

Предоставленная ссылка плохо разбирает пробелы. альтернативный ответ на вопрос SO в stackoverflow.com/questions/731649/…

Brent 10.08.2014 07:53

Теперь образец можно найти здесь: github.com/ceee/ReadSharp/blob/master/ReadSharp/…

StuartQ 13.07.2018 14:59

Чтобы добавить к ответу vfilby, вы можете просто выполнить замену RegEx в своем коде; новые классы не нужны. На случай, если другие новички, как я, наткнутся на этот вопрос.

using System.Text.RegularExpressions;

Затем...

private string StripHtml(string source)
{
        string output;

        //get rid of HTML tags
        output = Regex.Replace(source, "<[^>]*>", string.Empty);

        //get rid of multiple blank lines
        output = Regex.Replace(output, @"^\s*$\n", string.Empty, RegexOptions.Multiline);

        return output;
}

НЕХОРОШО! Его можно обмануть, чтобы он содержал скрипт, опуская закрывающую угловую скобку. РЕБЯТА, никогда не заносите в черный список. Вы не могу дезинфицируете ввод путем внесения в черный список. Это так неправильно.

usr 26.05.2011 22:11
stackoverflow.com/a/1732454/193892
Prof. Falken 19.02.2021 21:13

Я думаю, что самый простой способ - создать метод расширения строки (на основе того, что предложил пользователь Ричард):

using System;
using System.Text.RegularExpressions;

public static class StringHelpers
{
    public static string StripHTML(this string HTMLText)
        {
            var reg = new Regex("<[^>]+>", RegexOptions.IgnoreCase);
            return reg.Replace(HTMLText, "");
        }
}

Затем просто используйте этот метод расширения для любой строковой переменной в вашей программе:

var yourHtmlString = "<div class=\"someclass\"><h2>yourHtmlText</h2></span>";
var yourTextString = yourHtmlString.StripHTML();

Я использую этот метод расширения для преобразования комментариев в формате html в обычный текст, чтобы он правильно отображался в кристаллическом отчете, и он отлично работает!

stackoverflow.com/a/1732454/193892
Prof. Falken 19.02.2021 21:14

В HtmlAgilityPack нет метода с именем ConvertToPlainText, но вы можете преобразовать строку html в строку CLEAR с помощью:

HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(htmlString);
var textString = doc.DocumentNode.InnerText;
Regex.Replace(textString , @"<(.|n)*?>", string.Empty).Replace("&nbsp", "");

Это работает для меня. НО Я НЕ НАШЕЛ МЕТОД С ИМЯ «ConvertToPlainText» В «HtmlAgilityPack».

stackoverflow.com/a/1732454/193892
Prof. Falken 19.02.2021 21:14

хорошо, это не очень хорошо - поскольку вы используете дополнительную библиотеку только для поиска корневого узла документа, а затем применяете регулярное выражение ко всему корневому узлу? Либо вы используете HtmlAgilityPack для разбора html узла за узлом, либо используете регулярное выражение для обработки всего текста в целом.

Giedrius 26.02.2021 16:51

Я не мог использовать HtmlAgilityPack, поэтому написал для себя второе лучшее решение

private static string HtmlToPlainText(string html)
{
    const string tagWhiteSpace = @"(>|$)(\W|\n|\r)+<";//matches one or more (white space or line breaks) between '>' and '<'
    const string stripFormatting = @"<[^>]*(>|$)";//match any character between '<' and '>', even when end tag is missing
    const string lineBreak = @"<(br|BR)\s{0,1}/{0,1}>";//matches: <br>,<br/>,<br />,<BR>,<BR/>,<BR />
    var lineBreakRegex = new Regex(lineBreak, RegexOptions.Multiline);
    var stripFormattingRegex = new Regex(stripFormatting, RegexOptions.Multiline);
    var tagWhiteSpaceRegex = new Regex(tagWhiteSpace, RegexOptions.Multiline);

    var text = html;
    //Decode html specific characters
    text = System.Net.WebUtility.HtmlDecode(text); 
    //Remove tag whitespace/line breaks
    text = tagWhiteSpaceRegex.Replace(text, "><");
    //Replace <br /> with line breaks
    text = lineBreakRegex.Replace(text, Environment.NewLine);
    //Strip formatting
    text = stripFormattingRegex.Replace(text, string.Empty);

    return text;
}

& lt; blabla & gt; был проанализирован, поэтому я переместил text = System.Net.WebUtility.HtmlDecode (text); в конец метода

Luuk 20.08.2014 14:34

Это было здорово, я также добавил конденсатор с несколькими пространствами, так как html мог быть сгенерирован из CMS: var spaceRegex = new Regex ("[] {2,}", RegexOptions.None);

Enkode 03.04.2016 11:02

Иногда в html-коде есть новая строка кодера (новую строку нельзя увидеть в комментарии, поэтому я показываю ее с помощью [новой строки], например: <br> Я [новая строка] пропускаю [новую строку] you <br >, Значит, он должен показать: «Я скучаю по тебе», но он показывает, что я [новая строка] скучаю [новая строка] по тебе. Это заставляет простой текст выглядеть болезненно. Вы знаете, как исправить?

123iamking 07.06.2016 10:24

@ 123iamking можно использовать перед текстом возврата; : text.Replace ("[новая строка]", "\ n");

Eslam Badawy 20.10.2018 19:51

Я использовал это и понял, что иногда он оставляет '>' в начале строк. Другое решение применения регулярного выражения <[^>] *> отлично работает.

Etienne Charland 04.03.2019 19:51

Regex находится в System.Text.RegularExpressions

Eric Barr 29.06.2019 02:11
stackoverflow.com/a/1732454/193892
Prof. Falken 19.02.2021 21:17

Самый простой способ нашел:

HtmlFilter.ConvertToPlainText(html);

Класс HtmlFilter находится в Microsoft.TeamFoundation.WorkItemTracking.Controls.dll

DLL можно найти в такой папке: % ProgramFiles% \ Common Files \ microsoft shared \ Team Foundation Server \ 14.0 \

В VS 2015 dll также требует ссылки на Microsoft.TeamFoundation.WorkItemTracking.Common.dll, расположенный в той же папке.

заботится ли он о тегах скрипта, выделяется ли он полужирным курсивом и т. д.?

Samra 09.05.2017 04:43

Представление зависимости от основы команды для преобразования html в простой текст, очень сомнительно ...

ViRuSTriNiTy 17.03.2020 12:00

Трехэтапный процесс преобразования HTML в простой текст

Сначала вам нужно установить пакет Nuget для HtmlAgilityPack Второй Create This class

public class HtmlToText
{
    public HtmlToText()
    {
    }

    public string Convert(string path)
    {
        HtmlDocument doc = new HtmlDocument();
        doc.Load(path);

        StringWriter sw = new StringWriter();
        ConvertTo(doc.DocumentNode, sw);
        sw.Flush();
        return sw.ToString();
    }

    public string ConvertHtml(string html)
    {
        HtmlDocument doc = new HtmlDocument();
        doc.LoadHtml(html);

        StringWriter sw = new StringWriter();
        ConvertTo(doc.DocumentNode, sw);
        sw.Flush();
        return sw.ToString();
    }

    private void ConvertContentTo(HtmlNode node, TextWriter outText)
    {
        foreach(HtmlNode subnode in node.ChildNodes)
        {
            ConvertTo(subnode, outText);
        }
    }

    public void ConvertTo(HtmlNode node, TextWriter outText)
    {
        string html;
        switch(node.NodeType)
        {
            case HtmlNodeType.Comment:
                // don't output comments
                break;

            case HtmlNodeType.Document:
                ConvertContentTo(node, outText);
                break;

            case HtmlNodeType.Text:
                // script and style must not be output
                string parentName = node.ParentNode.Name;
                if ((parentName == "script") || (parentName == "style"))
                    break;

                // get text
                html = ((HtmlTextNode)node).Text;

                // is it in fact a special closing node output as text?
                if (HtmlNode.IsOverlappedClosingElement(html))
                    break;

                // check the text is meaningful and not a bunch of whitespaces
                if (html.Trim().Length > 0)
                {
                    outText.Write(HtmlEntity.DeEntitize(html));
                }
                break;

            case HtmlNodeType.Element:
                switch(node.Name)
                {
                    case "p":
                        // treat paragraphs as crlf
                        outText.Write("\r\n");
                        break;
                }

                if (node.HasChildNodes)
                {
                    ConvertContentTo(node, outText);
                }
                break;
        }
    }
}

Используя приведенный выше класс со ссылкой на ответ Иуды Химанго

В-третьих, вам нужно создать объект класса выше и использовать метод ConvertHtml(HTMLContent) для преобразования HTML в обычный текст, а не ConvertToPlainText(string html);.

HtmlToText htt=new HtmlToText();
var plainText = htt.ConvertHtml(HTMLContent);

Могу ли я пропустить преобразование ссылок в HTML. мне нужно сохранить ссылки в HTML при преобразовании в текст?

coder771 06.04.2018 11:03

Вот мое решение:

public string StripHTML(string html)
{
    if (string.IsNullOrWhiteSpace(html)) return "";

    // could be stored in static variable
    var regex = new Regex("<[^>]+>|\\s{2}", RegexOptions.IgnoreCase);
    return System.Web.HttpUtility.HtmlDecode(regex.Replace(html, ""));
}

Пример:

StripHTML("<p class='test' style='color:red;'>Here is my solution:</p>");
// output -> Here is my solution:

У меня был тот же вопрос, только у моего html был простой заранее известный макет, например:

<DIV><P>abc</P><P>def</P></DIV>

В итоге я использовал такой простой код:

string.Join (Environment.NewLine, XDocument.Parse (html).Root.Elements ().Select (el => el.Value))

Какие выходы:

abc
def

Не писал, а использовал:

using HtmlAgilityPack;
using System;
using System.IO;
using System.Text.RegularExpressions;

namespace foo {
  //small but important modification to class https://github.com/zzzprojects/html-agility-pack/blob/master/src/Samples/Html2Txt/HtmlConvert.cs
  public static class HtmlToText {

    public static string Convert(string path) {
      HtmlDocument doc = new HtmlDocument();
      doc.Load(path);
      return ConvertDoc(doc);
    }

    public static string ConvertHtml(string html) {
      HtmlDocument doc = new HtmlDocument();
      doc.LoadHtml(html);
      return ConvertDoc(doc);
    }

    public static string ConvertDoc(HtmlDocument doc) {
      using (StringWriter sw = new StringWriter()) {
        ConvertTo(doc.DocumentNode, sw);
        sw.Flush();
        return sw.ToString();
      }
    }

    internal static void ConvertContentTo(HtmlNode node, TextWriter outText, PreceedingDomTextInfo textInfo) {
      foreach (HtmlNode subnode in node.ChildNodes) {
        ConvertTo(subnode, outText, textInfo);
      }
    }
    public static void ConvertTo(HtmlNode node, TextWriter outText) {
      ConvertTo(node, outText, new PreceedingDomTextInfo(false));
    }
    internal static void ConvertTo(HtmlNode node, TextWriter outText, PreceedingDomTextInfo textInfo) {
      string html;
      switch (node.NodeType) {
        case HtmlNodeType.Comment:
          // don't output comments
          break;
        case HtmlNodeType.Document:
          ConvertContentTo(node, outText, textInfo);
          break;
        case HtmlNodeType.Text:
          // script and style must not be output
          string parentName = node.ParentNode.Name;
          if ((parentName == "script") || (parentName == "style")) {
            break;
          }
          // get text
          html = ((HtmlTextNode)node).Text;
          // is it in fact a special closing node output as text?
          if (HtmlNode.IsOverlappedClosingElement(html)) {
            break;
          }
          // check the text is meaningful and not a bunch of whitespaces
          if (html.Length == 0) {
            break;
          }
          if (!textInfo.WritePrecedingWhiteSpace || textInfo.LastCharWasSpace) {
            html = html.TrimStart();
            if (html.Length == 0) { break; }
            textInfo.IsFirstTextOfDocWritten.Value = textInfo.WritePrecedingWhiteSpace = true;
          }
          outText.Write(HtmlEntity.DeEntitize(Regex.Replace(html.TrimEnd(), @"\s{2,}", " ")));
          if (textInfo.LastCharWasSpace = char.IsWhiteSpace(html[html.Length - 1])) {
            outText.Write(' ');
          }
          break;
        case HtmlNodeType.Element:
          string endElementString = null;
          bool isInline;
          bool skip = false;
          int listIndex = 0;
          switch (node.Name) {
            case "nav":
              skip = true;
              isInline = false;
              break;
            case "body":
            case "section":
            case "article":
            case "aside":
            case "h1":
            case "h2":
            case "header":
            case "footer":
            case "address":
            case "main":
            case "div":
            case "p": // stylistic - adjust as you tend to use
              if (textInfo.IsFirstTextOfDocWritten) {
                outText.Write("\r\n");
              }
              endElementString = "\r\n";
              isInline = false;
              break;
            case "br":
              outText.Write("\r\n");
              skip = true;
              textInfo.WritePrecedingWhiteSpace = false;
              isInline = true;
              break;
            case "a":
              if (node.Attributes.Contains("href")) {
                string href = node.Attributes["href"].Value.Trim();
                if (node.InnerText.IndexOf(href, StringComparison.InvariantCultureIgnoreCase) == -1) {
                  endElementString = "<" + href + ">";
                }
              }
              isInline = true;
              break;
            case "li":
              if (textInfo.ListIndex > 0) {
                outText.Write("\r\n{0}.\t", textInfo.ListIndex++);
              } else {
                outText.Write("\r\n*\t"); //using '*' as bullet char, with tab after, but whatever you want eg "\t->", if utf-8 0x2022
              }
              isInline = false;
              break;
            case "ol":
              listIndex = 1;
              goto case "ul";
            case "ul": //not handling nested lists any differently at this stage - that is getting close to rendering problems
              endElementString = "\r\n";
              isInline = false;
              break;
            case "img": //inline-block in reality
              if (node.Attributes.Contains("alt")) {
                outText.Write('[' + node.Attributes["alt"].Value);
                endElementString = "]";
              }
              if (node.Attributes.Contains("src")) {
                outText.Write('<' + node.Attributes["src"].Value + '>');
              }
              isInline = true;
              break;
            default:
              isInline = true;
              break;
          }
          if (!skip && node.HasChildNodes) {
            ConvertContentTo(node, outText, isInline ? textInfo : new PreceedingDomTextInfo(textInfo.IsFirstTextOfDocWritten) { ListIndex = listIndex });
          }
          if (endElementString != null) {
            outText.Write(endElementString);
          }
          break;
      }
    }
  }
  internal class PreceedingDomTextInfo {
    public PreceedingDomTextInfo(BoolWrapper isFirstTextOfDocWritten) {
      IsFirstTextOfDocWritten = isFirstTextOfDocWritten;
    }
    public bool WritePrecedingWhiteSpace { get; set; }
    public bool LastCharWasSpace { get; set; }
    public readonly BoolWrapper IsFirstTextOfDocWritten;
    public int ListIndex { get; set; }
  }
  internal class BoolWrapper {
    public BoolWrapper() { }
    public bool Value { get; set; }
    public static implicit operator bool(BoolWrapper boolWrapper) {
      return boolWrapper.Value;
    }
    public static implicit operator BoolWrapper(bool boolWrapper) {
      return new BoolWrapper { Value = boolWrapper };
    }
  }
}

У него есть ограничение, заключающееся в том, что он не сжимает длинные строчные пробелы, но он определенно переносим и уважает макет, как веб-браузер.

static string HtmlToPlainText(string html) {
  string buf;
  string block = "address|article|aside|blockquote|canvas|dd|div|dl|dt|" +
    "fieldset|figcaption|figure|footer|form|h\\d|header|hr|li|main|nav|" +
    "noscript|ol|output|p|pre|section|table|tfoot|ul|video";

  string patNestedBlock = $"(\\s*?</?({block})[^>]*?>)+\\s*";
  buf = Regex.Replace(html, patNestedBlock, "\n", RegexOptions.IgnoreCase);

  // Replace br tag to newline.
  buf = Regex.Replace(buf, @"<(br)[^>]*>", "\n", RegexOptions.IgnoreCase);

  // (Optional) remove styles and scripts.
  buf = Regex.Replace(buf, @"<(script|style)[^>]*?>.*?</\1>", "", RegexOptions.Singleline);

  // Remove all tags.
  buf = Regex.Replace(buf, @"<[^>]*(>|$)", "", RegexOptions.Multiline);

  // Replace HTML entities.
  buf = WebUtility.HtmlDecode(buf);
  return buf;
}
stackoverflow.com/a/1732454/193892
Prof. Falken 19.02.2021 21:14

@ Профессор Фалькен, признаю. Я думаю, что у каждого кода есть свои плюсы и минусы. Его минусы - солидность, а плюсы - простота (в отношении sloc). Вы можете опубликовать код, используя XDocument.

jeiea 20.02.2021 22:26

Я столкнулся с подобной проблемой и нашел лучшее решение. Код ниже работает идеально для меня.

  private string ConvertHtml_Totext(string source)
    {
     try
      {
      string result;

    // Remove HTML Development formatting
    // Replace line breaks with space
    // because browsers inserts space
    result = source.Replace("\r", " ");
    // Replace line breaks with space
    // because browsers inserts space
    result = result.Replace("\n", " ");
    // Remove step-formatting
    result = result.Replace("\t", string.Empty);
    // Remove repeating spaces because browsers ignore them
    result = System.Text.RegularExpressions.Regex.Replace(result,
                                                          @"( )+", " ");

    // Remove the header (prepare first by clearing attributes)
    result = System.Text.RegularExpressions.Regex.Replace(result,
             @"<( )*head([^>])*>","<head>",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);
    result = System.Text.RegularExpressions.Regex.Replace(result,
             @"(<( )*(/)( )*head( )*>)","</head>",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);
    result = System.Text.RegularExpressions.Regex.Replace(result,
             "(<head>).*(</head>)",string.Empty,
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);

    // remove all scripts (prepare first by clearing attributes)
    result = System.Text.RegularExpressions.Regex.Replace(result,
             @"<( )*script([^>])*>","<script>",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);
    result = System.Text.RegularExpressions.Regex.Replace(result,
             @"(<( )*(/)( )*script( )*>)","</script>",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);
    //result = System.Text.RegularExpressions.Regex.Replace(result,
    //         @"(<script>)([^(<script>\.</script>)])*(</script>)",
    //         string.Empty,
    //         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
    result = System.Text.RegularExpressions.Regex.Replace(result,
             @"(<script>).*(</script>)",string.Empty,
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);

    // remove all styles (prepare first by clearing attributes)
    result = System.Text.RegularExpressions.Regex.Replace(result,
             @"<( )*style([^>])*>","<style>",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);
    result = System.Text.RegularExpressions.Regex.Replace(result,
             @"(<( )*(/)( )*style( )*>)","</style>",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);
    result = System.Text.RegularExpressions.Regex.Replace(result,
             "(<style>).*(</style>)",string.Empty,
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);

    // insert tabs in spaces of <td> tags
    result = System.Text.RegularExpressions.Regex.Replace(result,
             @"<( )*td([^>])*>","\t",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);

    // insert line breaks in places of <BR> and <LI> tags
    result = System.Text.RegularExpressions.Regex.Replace(result,
             @"<( )*br( )*>","\r",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);
    result = System.Text.RegularExpressions.Regex.Replace(result,
             @"<( )*li( )*>","\r",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);

    // insert line paragraphs (double line breaks) in place
    // if <P>, <DIV> and <TR> tags
    result = System.Text.RegularExpressions.Regex.Replace(result,
             @"<( )*div([^>])*>","\r\r",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);
    result = System.Text.RegularExpressions.Regex.Replace(result,
             @"<( )*tr([^>])*>","\r\r",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);
    result = System.Text.RegularExpressions.Regex.Replace(result,
             @"<( )*p([^>])*>","\r\r",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);

    // Remove remaining tags like <a>, links, images,
    // comments etc - anything that's enclosed inside < >
    result = System.Text.RegularExpressions.Regex.Replace(result,
             @"<[^>]*>",string.Empty,
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);

    // replace special characters:
    result = System.Text.RegularExpressions.Regex.Replace(result,
             @" "," ",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);

    result = System.Text.RegularExpressions.Regex.Replace(result,
             @"&bull;"," * ",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);
    result = System.Text.RegularExpressions.Regex.Replace(result,
             @"&lsaquo;","<",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);
    result = System.Text.RegularExpressions.Regex.Replace(result,
             @"&rsaquo;",">",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);
    result = System.Text.RegularExpressions.Regex.Replace(result,
             @"&trade;","(tm)",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);
    result = System.Text.RegularExpressions.Regex.Replace(result,
             @"&frasl;","/",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);
    result = System.Text.RegularExpressions.Regex.Replace(result,
             @"&lt;","<",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);
    result = System.Text.RegularExpressions.Regex.Replace(result,
             @"&gt;",">",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);
    result = System.Text.RegularExpressions.Regex.Replace(result,
             @"&copy;","(c)",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);
    result = System.Text.RegularExpressions.Regex.Replace(result,
             @"&reg;","(r)",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);
    // Remove all others. More can be added, see
    // http://hotwired.lycos.com/webmonkey/reference/special_characters/
    result = System.Text.RegularExpressions.Regex.Replace(result,
             @"&(.{2,6});", string.Empty,
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);

    // for testing
    //System.Text.RegularExpressions.Regex.Replace(result,
    //       this.txtRegex.Text,string.Empty,
    //       System.Text.RegularExpressions.RegexOptions.IgnoreCase);

    // make line breaking consistent
    result = result.Replace("\n", "\r");

    // Remove extra line breaks and tabs:
    // replace over 2 breaks with 2 and over 4 tabs with 4.
    // Prepare first to remove any whitespaces in between
    // the escaped characters and remove redundant tabs in between line breaks
    result = System.Text.RegularExpressions.Regex.Replace(result,
             "(\r)( )+(\r)","\r\r",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);
    result = System.Text.RegularExpressions.Regex.Replace(result,
             "(\t)( )+(\t)","\t\t",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);
    result = System.Text.RegularExpressions.Regex.Replace(result,
             "(\t)( )+(\r)","\t\r",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);
    result = System.Text.RegularExpressions.Regex.Replace(result,
             "(\r)( )+(\t)","\r\t",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);
    // Remove redundant tabs
    result = System.Text.RegularExpressions.Regex.Replace(result,
             "(\r)(\t)+(\r)","\r\r",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);
    // Remove multiple tabs following a line break with just one tab
    result = System.Text.RegularExpressions.Regex.Replace(result,
             "(\r)(\t)+","\r\t",
             System.Text.RegularExpressions.RegexOptions.IgnoreCase);
    // Initial replacement target string for line breaks
    string breaks = "\r\r\r";
    // Initial replacement target string for tabs
    string tabs = "\t\t\t\t\t";
    for (int index=0; index<result.Length; index++)
    {
        result = result.Replace(breaks, "\r\r");
        result = result.Replace(tabs, "\t\t\t\t");
        breaks = breaks + "\r";
        tabs = tabs + "\t";
    }

    // That's it.
    return result;
}
catch
{
    MessageBox.Show("Error");
    return source;
}

}

Сначала нужно было удалить escape-символы, такие как \ n и \ r, потому что они приводят к тому, что регулярные выражения перестают работать должным образом.

Более того, чтобы строка результата правильно отображалась в текстовом поле, может потребоваться разделить ее и установить свойство Lines текстового поля вместо присвоения свойству Text.

this.txtResult.Lines = StripHTML (this.txtSource.Text) .Split ("\ r" .ToCharArray ());

Источник: https://www.codeproject.com/Articles/11902/Convert-HTML-to-Plain-Text-2

stackoverflow.com/a/1732454/193892
Prof. Falken 19.02.2021 21:14

Думаю, у него есть простой ответ:

public string RemoveHTMLTags(string HTMLCode)
{
    string str=System.Text.RegularExpressions.Regex.Replace(HTMLCode, "<[^>]*>", "");
    return str;
}

Для тех, кто ищет точное решение вопроса OP для текстового сокращения данного html-документа без символов новой строки и HTML-тегов, пожалуйста, найдите решение ниже.

Как и в случае любого предлагаемого решения, в приведенном ниже коде есть некоторые предположения:

  • теги сценария или стиля не должны содержать теги сценария и стиля в качестве часть сценария
  • будут встроены только основные встроенные элементы без пробел, то есть he<span>ll</span>o должен выводить hello. Список встроенных теги: https://www.w3schools.com/htmL/html_blocks.asp

Учитывая вышеизложенное, следующее расширение строки со скомпилированными регулярными выражениями будет выводить ожидаемый простой текст с учетом экранированных символов html и null при вводе null.

public static class StringExtensions
{
    public static string ConvertToPlain(this string html)
    {
        if (html == null)
        {
            return html;
        }

        html = scriptRegex.Replace(html, string.Empty);
        html = inlineTagRegex.Replace(html, string.Empty);
        html = tagRegex.Replace(html, " ");
        html = HttpUtility.HtmlDecode(html);
        html = multiWhitespaceRegex.Replace(html, " ");

        return html.Trim();
    }

    private static readonly Regex inlineTagRegex = new Regex("<\/?(a|span|sub|sup|b|i|strong|small|big|em|label|q)[^>]*>", RegexOptions.Compiled | RegexOptions.Singleline);
    private static readonly Regex scriptRegex = new Regex("<(script|style)[^>]*?>.*?</\\1>", RegexOptions.Compiled | RegexOptions.Singleline);
    private static readonly Regex tagRegex = new Regex("<[^>]+>", RegexOptions.Compiled | RegexOptions.Singleline);
    private static readonly Regex multiWhitespaceRegex = new Regex("\\s+", RegexOptions.Compiled | RegexOptions.Singleline);
}

Другие вопросы по теме