Как конвертировать Html в обычный текст?


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

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

Как разместить "текст" в этом Html в строку как прямой текст?

Итак, этот кусок код.

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

будет:

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

16 68

16 ответов:

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

<[^>]*>

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

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

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

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

var plainText = ConvertToPlainText(string html);

кормить его HTML-строку, как

Привет мир!
это я! !

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

hello world!
it is me!

Я не мог использовать 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;
}

HTTPUtility.HTMLEncode() предназначен для обработки HTML-тегов кодирования в виде строк. Он заботится обо всех тяжелых подъемных для вас. Из Примечание:

если символы, такие как пробелы и знаки препинания передаются в потоке HTTP, они могут быть неверно истолкованы на принимающей стороне. Кодировка HTML преобразует символы, которые не разрешены в HTML, в эквиваленты символов-сущностей; декодирование HTML отменяет кодировку. Например, при внедрении в блок текст, символы < и >, кодируются как &lt; и &gt; для передачи HTTP.

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();

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

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;
}

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

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

это работает для меня. Но я не нахожу метод с именем 'ConvertToPlainText' в 'HtmlAgilityPack'.

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

сначала вам нужно установить пакет Nuget для HtmlAgilityPack Во-вторых создать этот класс

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);

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

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)[^>]*?>.*?</>", "", RegexOptions.Singleline);

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

  // Replace HTML entities.
  buf = WebUtility.HtmlDecode(buf);
  return buf;
}

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

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, "");
        }
}

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

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

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

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

HtmlFilter.ConvertToPlainText(html);

класс HtmlFilter находится в Microsoft.TeamFoundation.Модуле.Контроли.dll

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

в VS 2015 dll также требует Ссылки на Microsoft.TeamFoundation.Модуле.Общий.dll, находящаяся в той же папке.

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

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

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

зависит от того, что вы подразумеваете под "html."Самым сложным случаем были бы полные веб-страницы. Это также Самый простой в обращении, так как вы можете использовать текстовый веб-браузер. Смотрите статья в Википедии список веб-браузеров, включая браузеры, текстовый режим. Рысь, вероятно, самый известный, но один из других может быть лучше для ваших нужд.

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

public string StripHTML(string html)
{
    var regex = new Regex("<[^>]+>", 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 };
    }
  }
}

public static string StripTags2(string html) { возвращают HTML-код.Заменять.)"" ,">"("); }

этим вы избегаете всех " " в строке. Это то, чего ты хочешь?