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

C# 的文本模板类

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.14/5 (12投票s)

2004 年 12 月 9 日

CPOL

3分钟阅读

viewsIcon

97016

downloadIcon

1359

一个使用 RegEx 回调函数的 .NET 文本模板简单类。

引言

可以使用 RegEx.Replace() 方法的 MatchEvaluator 委托在 C# 中实现一个基于标签的模板系统。这些标签存储在哈希表中,键是标签本身,替换文本是 TemplateTag 对象的 Value。操作哈希表和解析模板的代码包含在单个类 TemplateParser 中。

背景

我广泛使用 ASPTemplate 进行 ASP 开发和代码生成,因此我自然而然地开始寻找 C# 中类似的东西。在我的搜索过程中,我遇到了 Jack Herrington 撰写的文章 Ruby 中的模板,其中结合了正则表达式和哈希表来构建一个简单的模板系统。由于我找不到任何其他满足我需求的基于 C# 的标签模板系统,所以我决定按照相同的想法自己编写一个。

目标是能够获取包含标签的模板文档,并有一种简单的方法用值替换这些标签。模板文档看起来像

Tag1 == [%tag1%]    Tag2 == [%tag2%]
The sky is [%SkyColor%].
Today is [%TodayDate%].

其中标签由 [%...%] 标记分隔。如果我们将标签设置为以下值

[%tag1%] = This is Tag 1
[%tag2%] = This is not Tag 1 but Tag 2
[%SkyColor%] = blue
[%TodayDate%] = 11/19/2004

那么模板解析器的最终输出应该为

Tag1 == This is Tag 1    Tag2 == This is not Tag 1 but Tag 2
The sky is blue.
Today is 11/19/2004.

代码

TemplateParser 类包含存储标签和处理模板的所有代码。这些标签存储在 HashTable _templateTags 中。

private Hashtable _templateTags = new Hashtable();

HashTable 包含一个 TemplateTag 对象集合。TemplateTag 对象包含两个 string 属性,TagValueTag 用作哈希表键,而 Value 用于替换模板中的标签。

模板标签通过重载的 public 方法 AddTag() 添加到哈希表。可以使用 RemoveTag() 方法删除单个标签,或者可以使用 ClearTags() 方法清除所有标签。

    public void AddTag( TemplateTag templateTag )
    {
        _templateTags[templateTag.Tag] = templateTag;
    }
    
    public void AddTag( string Tag, string Value )
    {
        AddTag( new TemplateTag( Tag, Value ) );
    }
    
    public void RemoveTag( string Tag )
    {
        _templateTags.Remove( Tag );
    }

    public void ClearTags()
    {
        _templateTags.Clear();
    }

默认的标签样式是 [%...%],并且使用正则表达式 @"(\[%\w+%\])" 找到这些标签。这可以通过更改 TemplateParserMatchPattern 属性来更改。

Regex.Replace 函数具有一个接受委托函数的重载运算符。委托函数需要传递一个 System.Text.RegularExpressions.Match 参数,并返回一个 string

NetTemplate 的委托函数检查 Match.Value(即模板标签)是否在标签 HashTable 中。如果标签存在,则从 HashTable 返回标签的 Value,否则返回一个空字符串。

    private string _replaceTagHandler( Match token )
    {
        if ( _templateTags.Contains( token.Value ) )
            return ((TemplateTag) _templateTags[token.Value]).Value;
        else
            return string.Empty;
    }

public ParseTemplateString() 方法获取要解析的字符串并返回已解析的字符串。这允许您处理单个字符串,而不仅仅局限于处理模板文件。

    public string ParseTemplateString( string Template )
    {
        MatchEvaluator replaceCallback = new MatchEvaluator( _replaceTagHandler );
        string newString = Regex.Replace( Template, _matchPattern, replaceCallback );

        return newString;
    }

ParseTemplateFile() 获取模板的文件名,将内容读入文件缓冲区,然后将缓冲区传递给 ParseTemplateString()

    public string ParseTemplateFile( string TemplateFilename )
    {
        string fileBuffer = _fileToBuffer( TemplateFilename );
        return ParseTemplateString( fileBuffer ); 
    } 
    
    private string _fileToBuffer( string Filename ) 
    {
        if( !File.Exists( Filename ) ) 
            throw new ArgumentNullException( Filename, 
                  "Template file does not exist" ); 
            
        StreamReader reader = new StreamReader( Filename );
        string fileBuffer = reader.ReadToEnd();
        reader.Close();

        return fileBuffer;
    }

使用 TemplateParser

要解析模板字符串,请创建 TemplateParser 的一个实例,添加标签和值,然后执行方法 ParseTemplateString()

   public void SimpleParse() 
    {

        TemplateParser tp = new TemplateParser();
        tp.AddTag( new TemplateTag( "[%Title%]", "Template Test" ) );
        
        string inputString = @"This is a [%Title%] or is it.";
        
        string outputString = tp.ParseTemplateString(inputString);

        string expectedResults = @"This is a Template Test or is it.";
        
        Assert.AreEqual( expectedResults, outputString);
    }

要解析模板文件,唯一需要的区别是将文件名传递给 ParseTemplateFile() 方法

    public void TemplateFileTester()
    {
        string TemplateFilename = @".\TemplateTest.txt";
        
        TemplateParser tp = new TemplateParser();
        tp.AddTag( new TemplateTag( "[%test1%]", "test1 SAT") );
        tp.AddTag( new TemplateTag( "[%test2%]", "test2 SAT") );
        tp.AddTag( new TemplateTag( "[%test3%]", "test3 SAT") );
        tp.AddTag( new TemplateTag( "[%test4%]", "test4 SAT") );
        tp.AddTag( new TemplateTag( "[%test5%]", "test5 SAT") );
        tp.AddTag( new TemplateTag( "[%test6%]", "test6 SAT") );

        string parsedFile = tp.ParseTemplateFile(TemplateFilename);

        string TemplateOutput = @".\TemplateTestOut.txt";
        if (File.Exists(TemplateOutput)) File.Delete(TemplateOutput);
        StreamWriter writer = new StreamWriter(TemplateOutput);
        writer.Write(parsedFile);
        writer.Flush();
        writer.Close();
    }

关注点

我真的对这个模板系统的构建方式感到惊讶。诚然,我没有进行任何测量来查看它的运行速度,但是考虑到我的大多数模板文件都很小,这应该不是一个大问题。

我将来想进行的一个修改是为 TemplateTag 对象使用一个接口。这将允许我使用字符串值以外的更多内容作为模板。

历史

  • 2004 年 12 月 9 日:文章提交。
  • 2004 年 12 月 10 日:根据 SimmoTech 的更正和更改。
© . All rights reserved.