C# 将 HTML 转成纯文本
时间:2016年12月26日 人气:...

/// <summary>

/// Converts HTML to plain text.

/// </summary>

class HtmlToText

{

    // Static data tables

    protected static Dictionary<string, string> _tags;

    protected static HashSet<string> _ignoreTags;

  

    // Instance variables

    protected TextBuilder _text;

    protected string _html;

    protected int _pos;

  

    // Static constructor (one time only)

    static HtmlToText()

    {

        _tags = new Dictionary<string, string>();

        _tags.Add("address", " ");

        _tags.Add("blockquote", " ");

        _tags.Add("p", " ");

        _tags.Add("dl", " ");

        _tags.Add("fieldset", " ");

        _tags.Add("form", " ");

        _tags.Add("h1", " ");

        _tags.Add("/h1", " ");

        _tags.Add("h2", " ");

        _tags.Add("/h2", " ");

        _tags.Add("h3", " ");

        _tags.Add("/h3", " ");

        _tags.Add("h4", " ");

        _tags.Add("/h4", " ");

        _tags.Add("h5", " ");

        _tags.Add("/h5", " ");

        _tags.Add("h6", " ");

        _tags.Add("/h6", " ");

        _tags.Add("p", " ");

        _tags.Add("/p", " ");

        _tags.Add("table", " ");

        _tags.Add("/table", " ");

        _tags.Add("ul", " ");

        _tags.Add("/ul", " ");

        _tags.Add("ol", " ");

        _tags.Add("/ol", " ");

        _tags.Add("/li", " ");

        _tags.Add("br", " ");

        _tags.Add("/td", " ");

        _tags.Add("/tr", " ");

        _tags.Add("/pre", " ");

  

        _ignoreTags = new HashSet<string>();

        _ignoreTags.Add("script");

        _ignoreTags.Add("noscript");

        _ignoreTags.Add("style");

        _ignoreTags.Add("object");

    }

  

    /// <summary>

    /// Converts the given HTML to plain text and returns the result.

    /// </summary>

    /// <param name="html">HTML to be converted

    /// <returns>Resulting plain text</returns>

    public string Convert(string html)

    {

        // Initialize state variables

        _text = new TextBuilder();

        _html = html;

        _pos = 0;

  

        // Process input

        while (!EndOfText)

        {

            if (Peek() == '<')

            {

                // HTML tag

                bool selfClosing;

                string tag = ParseTag(out selfClosing);

  

                // Handle special tag cases

                if (tag == "body")

                {

                    // Discard content before 

                    _text.Clear();

                }

                else if (tag == "/body")

                {

                    // Discard content after 

                    _pos = _html.Length;

                }

                else if (tag == "pre")

                {

                    // Enter preformatted mode

                    _text.Preformatted = true;

                    EatWhitespaceToNextLine();

                }

                else if (tag == "/pre")

                {

                    // Exit preformatted mode

                    _text.Preformatted = false;

                }

  

                string value;

                if (_tags.TryGetValue(tag, out value))

                    _text.Write(value);

  

                if (_ignoreTags.Contains(tag))

                    EatInnerContent(tag);

            }

            else if (Char.IsWhiteSpace(Peek()))

            {

                // Whitespace (treat all as space)

                _text.Write(_text.Preformatted ? Peek() : ' ');

                MoveAhead();

            }

            else

            {

                // Other text

                _text.Write(Peek());

                MoveAhead();

            }

        }

        // Return result

        return HttpUtility.HtmlDecode(_text.ToString());

    }

  

    // Eats all characters that are part of the current tag

    // and returns information about that tag

    protected string ParseTag(out bool selfClosing)

    {

        string tag = String.Empty;

        selfClosing = false;

  

        if (Peek() == '<')

        {

            MoveAhead();

  

            // Parse tag name

            EatWhitespace();

            int start = _pos;

            if (Peek() == '/')

                MoveAhead();

            while (!EndOfText && !Char.IsWhiteSpace(Peek()) &&

                Peek() != '/' && Peek() != '>')

                MoveAhead();

            tag = _html.Substring(start, _pos - start).ToLower();

  

            // Parse rest of tag

            while (!EndOfText && Peek() != '>')

            {

                if (Peek() == '"' || Peek() == ''')

                    EatQuotedValue();

                else

                {

                    if (Peek() == '/')

                        selfClosing = true;

                    MoveAhead();

                }

            }

            MoveAhead();

        }

        return tag;

    }

  

    // Consumes inner content from the current tag

    protected void EatInnerContent(string tag)

    {

        string endTag = "/" + tag;

  

        while (!EndOfText)

        {

            if (Peek() == '<')

            {

                // Consume a tag

                bool selfClosing;

                if (ParseTag(out selfClosing) == endTag)

                    return;

                // Use recursion to consume nested tags

                if (!selfClosing && !tag.StartsWith("/"))

                    EatInnerContent(tag);

            }

            else MoveAhead();

        }

    }

  

    // Returns true if the current position is at the end of

    // the string

    protected bool EndOfText

    {

        get { return (_pos >= _html.Length); }

    }

  

    // Safely returns the character at the current position

    protected char Peek()

    {

        return (_pos < _html.Length) ? _html[_pos] : (char)0;

    }

  

    // Safely advances to current position to the next character

    protected void MoveAhead()

    {

        _pos = Math.Min(_pos + 1, _html.Length);

    }

  

    // Moves the current position to the next non-whitespace

    // character.

    protected void EatWhitespace()

    {

        while (Char.IsWhiteSpace(Peek()))

            MoveAhead();

    }

  

    // Moves the current position to the next non-whitespace

    // character or the start of the next line, whichever

    // comes first

    protected void EatWhitespaceToNextLine()

    {

        while (Char.IsWhiteSpace(Peek()))

        {

            char c = Peek();

            MoveAhead();

            if (c == ' ')

                break;

        }

    }

  

    // Moves the current position past a quoted value

    protected void EatQuotedValue()

    {

        char c = Peek();

        if (c == '"' || c == ''')

        {

            // Opening quote

            MoveAhead();

            // Find end of value

            int start = _pos;

            _pos = _html.IndexOfAny(new char[] { c, ' ', ' ' }, _pos);

            if (_pos < 0)

                _pos = _html.Length;

            else

                MoveAhead();    // Closing quote

        }

    }

  

    /// <summary>

    /// A StringBuilder class that helps eliminate excess whitespace.

    /// </summary>

    protected class TextBuilder

    {

        private StringBuilder _text;

        private StringBuilder _currLine;

        private int _emptyLines;

        private bool _preformatted;

  

        // Construction

        public TextBuilder()

        {

            _text = new StringBuilder();

            _currLine = new StringBuilder();

            _emptyLines = 0;

            _preformatted = false;

        }

  

        /// <summary>

        /// Normally, extra whitespace characters are discarded.

        /// If this property is set to true, they are passed

        /// through unchanged.

        /// </summary>

        public bool Preformatted

        {

            get

            {

                return _preformatted;

            }

            set

            {

                if (value)

                {

                    // Clear line buffer if changing to

                    // preformatted mode

                    if (_currLine.Length > 0)

                        FlushCurrLine();

                    _emptyLines = 0;

                }

                _preformatted = value;

            }

        }

  

        /// <summary>

        /// Clears all current text.

        /// </summary>

        public void Clear()

        {

            _text.Length = 0;

            _currLine.Length = 0;

            _emptyLines = 0;

        }

  

        /// <summary>

        /// Writes the given string to the output buffer.

        /// </summary>

        /// <param name="s">

        public void Write(string s)

        {

            foreach (char c in s)

                Write(c);

        }

  

        /// <summary>

        /// Writes the given character to the output buffer.

        /// </summary>

        /// <param name="c">Character to write

        public void Write(char c)

        {

            if (_preformatted)

            {

                // Write preformatted character

                _text.Append(c);

            }

            else

            {

                if (c == ' ')

                {

                    // Ignore carriage returns. We'll process

                    // ' ' if it comes next

                }

                else if (c == ' ')

                {

                    // Flush current line

                    FlushCurrLine();

                }

                else if (Char.IsWhiteSpace(c))

                {

                    // Write single space character

                    int len = _currLine.Length;

                    if (len == 0 || !Char.IsWhiteSpace(_currLine[len - 1]))

                        _currLine.Append(' ');

                }

                else

                {

                    // Add character to current line

                    _currLine.Append(c);

                }

            }

        }

  

        // Appends the current line to output buffer

        protected void FlushCurrLine()

        {

            // Get current line

            string line = _currLine.ToString().Trim();

  

            // Determine if line contains non-space characters

            string tmp = line.Replace(" ", String.Empty);

            if (tmp.Length == 0)

            {

                // An empty line

                _emptyLines++;

                if (_emptyLines < 2 && _text.Length > 0)

                    _text.AppendLine(line);

            }

            else

            {

                // A non-empty line

                _emptyLines = 0;

                _text.AppendLine(line);

            }

  

            // Reset current line

            _currLine.Length = 0;

        }

  

        /// <summary>

        /// Returns the current output as a string.

        /// </summary>

        public override string ToString()

        {

            if (_currLine.Length > 0)

                FlushCurrLine();

            return _text.ToString();

        }

    }

}</param name="c"></param name="s"></param name="html"></string></string, string></string></string, string>


热门评论