65.9K
CodeProject 正在变化。 阅读更多。
Home

一个简单的 HTML 标记解析器

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.90/5 (12投票s)

2008年7月21日

CPOL

3分钟阅读

viewsIcon

46573

downloadIcon

647

一个非常简单的 HTML 模板解析器,它用有意义的文本替换用户定义的标记。

引言

在开发我的最新网站时,我研究了许多 CMS 系统,并测试了在 Visual Studio 中生成动态 Web 内容的不同方法。似乎没有一个适合。我想要的是一种非常简单的方式来显示动态内容 - 一种简单的 API。我希望能够编辑基本的 HTML 文件来做到这一点。在研究了创建我自己的 HTTPhandler 的方法并决定这对我的应用程序来说是多余的之后,我回想起 Delphi 已经实现了一个优雅的解决方案:PageProducers。

自从最近转投 C#.NET 阵营以来,我决定重新创建一个简单的 Delphi 的 PageProducer 类的版本。我提出的 HTML 模板解析器提供了一种简单的方法来实现用户定义的 HTML 模板。它允许您定义自己的标记,并在运行时在代码中用您希望的值替换这些标记。它不是您见过的最复杂的解析器,但它以最少的麻烦完成了工作。

概述

调用代码实例化一个 TokenParser 类的对象,并为其分配一个事件处理程序。

  parser = new TokenParser(fileName); // fileName is the path and name of the HTML file.
  parser.OnToken += this.OnToken;
  
  public void OnToken(string strToken, ref string strReplacement)
  {
    if (strToken == "TESTME")
      strReplacement = "Moose Butts a-Flyin!";
    else
      strReplacement = "Huh?";
  }

完成此操作后,只需从 TokenParserToString() 方法获取已解析的 HTML 并在 Response 对象中返回它即可。

Using the Code

TokenParser 类

TokenParser 定义了一个委托,用于将找到的标记传递给调用代码。当解析器遇到标记时,它会调用 OnToken 事件,并将标记和一个引用的字符串传递给已实现的 TokenHandler,该处理程序会将所需的值放置到其中。

// ********************************************************************************
//     Document    :  TokenParser.cs
//     Version     :  0.1
//     Project     :  StrayIdeaz
//     Description :  This is a very simple HTML template parser. It takes a text file
//                    and replaces tokens with values supplied by the calling code
//                    via a delegate.
//     Author      :  StrayVision Software
//     Date        :  7/20/2008
// ********************************************************************************

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace TemplateParser
{

  /// <summary />
  ///     TokenParser is a class which implements a simple token replacement parser.
  /// </summary />
  /// <remarks />
  ///     TokenParser is used by the calling code by implementing an event handler for
  ///     the delegate TokenHandler(string strToken, ref string strReplacement)
  /// </remarks />
  public class TokenParser
  {
    private String inputText;
    private String textSourceFile;

    public delegate void TokenHandler(string strToken, ref string strReplacement);
    public event TokenHandler OnToken;

构造函数接受要解析的源文件的路径和名称。

    public TokenParser(String sourceFile)
    {
      textSourceFile = sourceFile;
    }

ExtractToken 解析格式为 "[%TOKENNAME%]" 的标记。它不知道标记的含义,只知道它是由 [% 和 %] 括起来的字符串。它返回这两个标记之间的字符串。

    private string ExtractToken(string strToken)
    {
      int firstPos = strToken.IndexOf("[%")+2;
      int secondPos = strToken.LastIndexOf("%]");
      string result = strToken.Substring(firstPos, secondPos - firstPos);
      
      return result.Trim();
    }

Parse() 遍历类变量 inputText 的每个字符。 inputText 代表要解析的 HTML 文件的内容。 Parse() 返回一个字符串,表示 inputText,其标记已替换为调用代码的值。

    private String Parse()
    {
      const string tokenStart = "[";
      const string tokenNext = "%";
      const string tokenEnd = "]";
     
      String outText = String.Empty;
      String token = String.Empty;
      String replacement = String.Empty;

      int i = 0;
      string tok;
      string tok2;
      int len = inputText.Length;

      while (i < len)
      {
        tok = inputText[i].ToString(); 
        if(tok == tokenStart)
        {
          i++;
          tok2 = inputText[i].ToString(); 
          if (tok2 == tokenNext)
          {
            i--;
            while (i < len & tok2 != tokenEnd)
            {
              tok2 = inputText[i].ToString();
              token += tok2;
              i++;
            }
            OnToken(ExtractToken(token), ref replacement);
            outText += replacement;
            token = String.Empty;
            tok = inputText[i].ToString();//i++;
          }
        }
        outText += tok;
        i++;
      }
      return outText;
    }

Content() 方法只是读取 HTML 文件并返回其未更改的文本。如果您编写了一个应用程序,其中包含教程,并且您想显示未解析的 HTML 以演示模板中标记的使用,这很有用。

    /// <summary />
    ///     Content() reads the text file specified in the constructor 
    ///     and returns the unparsed text.
    /// </summary />
    /// <returns />
    ///     A string representing the unparsed text file.
    /// </returns />
    public String Content()
    {
      string result;
      try
      {
        TextReader reader = new StreamReader(textSourceFile);
        inputText = reader.ReadToEnd();
        reader.Close();
        result = inputText;
      }
      catch (Exception e)
      {
        result = e.Message;
      }
      return result;      
    }

现在,我们来讨论重要的方法:ToString()。实现 OnToken 事件后,您调用 ToString() 来执行实际的解析,并获取已解析的 HTML 文本,其中填充了您的值。

    /// <summary />
    ///     This is called to return the parsed text file.
    /// </summary />
    /// <returns />
    ///     A string representing the text file with all its tokens replaced by data
    ///     supplied by the calling code through the Tokenhandler delegate
    /// </returns />
    public override string ToString()
    {
      //TextReader reader;
      string result;
      try
      {
        TextReader reader = new StreamReader(textSourceFile);
        inputText = reader.ReadToEnd();
        reader.Close();
        result = Parse();
      }
      catch (Exception e)
      {
        result = e.Message;
      }
      return result;      
    }

  }
}

现在我们已经看到了 TokenParser 类,让我们来看看如何使用它。下面列出了一个 ASP.NET 代码隐藏页面的完整源代码,该页面实现了 TokenParser

声明了两个类级别的私有变量:String pageText,它将保存已解析的 HTML 文本,以及 TokenParser parser,这是我们的 TokenParser 对象。在 Page_Load 事件中,我们获取了模板文件的路径(这可以是您想要的任何路径和文件名)。然后,我们调用 LoadPage() 来完成工作。

还要注意的是,我们实现了一个名为 OnToken 的方法。这是我们的事件处理程序。您可以将其命名为任何您想要的名称,只要它接受两个参数:stringref string。在这个事件中,替换标记为有意义的值的工作就完成了。

这些标记可以被称为您想要的任何名称:[%MYVAR%], [%FOOBAR%], [%LATESTNEWS%] 等等。根据传递的标记,您在 ref string 中发回 HTML 代码、JavaScript 或您想要的任何文本。

LoadPage() 是我们示例中的最后一个方法。这是实例化 parser 对象、分配 Ontoken 事件,以及调用 parser.ToString() 方法以检索我们已解析的 HTML 模板的地方。从那里,只需通过 Response.Write() 将其发送到用户的浏览器即可。

  public partial class _Default : System.Web.UI.Page
  {
    private String pageText;
    private TokenParser parser;

    protected void Page_Load(object sender, EventArgs e)
    {
      string path = Server.MapPath("template.html");
      LoadPage(path);
    }

    public void OnToken(string strToken, ref string strReplacement)
    {
      if (strToken == "TESTME")
        strReplacement = "Moose Butts a-Flyin!";
      else
        strReplacement = "Huh?";
    }

    private void LoadPage(string fileName)
    {
      parser = new TokenParser(fileName);
      parser.OnToken += this.OnToken;
      pageText = parser.ToString();
      DisplayPage();
    }
  }
© . All rights reserved.