站内搜索引擎(ASP.NET)

原文出自:http://www.codeproject.com/KB/applications/SearchDotnet.aspx

作者:Stevan Rodrigues

翻译:张远山

站点搜索引擎搜索整个页面(动态页面也可以)中的关键字或关键词并且计算关键字或关键词在页面中出现的次数,然后按最高匹配优先把结果显示出来。

 

l         下载最新版本的示例工程 (VB.NET) - 67 Kb

l         下载读写XML的示例工程 (VB.NET) - 88 Kb

l         下载最新版本的示例工程(Visual Studio .NET 非必须的) - 52 Kb

l         下载最早版本的示例工程 (C #) - 28 Kb

引言

站点搜索引擎搜索整个页面(动态页面也可以)中的关键字或关键词并且计算关键字或关键词在页面中出现的次数,然后按最高匹配优先把结果显示出来。你可以轻易地把文件扩展名放到web.congig文件标记的地方,搜索模块将搜索所有符合这些文件扩展名的文件。你不想搜索的文件或文件夹也可以放在web.config标记的地方,这些文件和文件夹将不会被搜索。现在你还可以选择编码格式。

这篇已更新的文章包含全球化和优化代码的提示

注:此搜索引擎适最合于小站点。你也可以使用正则表达式和改变这些代码来搜索内部网。对于大型网站你需要周期性的写入XML然后从XML中读取。我已经在文章的后面部分提供了使用说明。我也加入了一个读写XML的示例工程

背景

搜索引擎帮助用户找到他感兴趣的页面。当我在做ASP.NET项目的时候,我一定把搜索模块加到网站中去。我曾经有一个ASP的而非.NET的,这个搜索引擎就是因此而产生的。我的第一个版本仅仅是一个简单的Web窗体,还没有充分使用.NET 语言面向对象的特性。在空闲时间,我使我的代码最大限度地使用面向对象语言。在这篇文章中,我在实际经验和不同作者良好实践建议基础上进一步加强我的设计。

来自北京的 宋涛先生带着怎样把模块转成中文的疑问联系了我。在他的帮助下我增强代码可以支持其他语言。一些用户在把SiteSearch.asp放到网站根目录的时候也遇到了一些问题,我已经更改了代码修正了错误。

源代码概述

网站搜索引擎结构如下所示:

定义类和创建类示例的能力是面向对象语言的最重要的特性之一。在紧接着的章节中,我们将讲述搜索模块中用到的类。

 

 

 

类名

描述

SiteSearch

Web窗体使用的类,用户可以通过某些词搜索网站

Searches.CleanHtml

用来清理HTML内容的类

Searches.FileContent

HTML文件获取内容的类

Searches.Page

用来存储页面中数据的类

Searches.PagesDataset

用来在dataset中创建和存储结果的类

Searches.Site

用来读取网站配置的类

Searches.UserSearch

用来为每个用户存储搜索信息的类

SiteSearch.aspx

Web窗体是微软的.NET首创的令人激动的特性之一。SiteSearch.aspx是一个Web窗体也是搜索模块的起始页面。

一个Web窗体页面由页面(ASPX 文件)和后台代码(.aspx.cs 文件或.aspx.vb文件)组成。我们的Web窗体由SiteSearch..aspxSiteSearch.aspx.vb。下面我将处理这两个页面同时涉及一些Web窗体的主要元素。

ASP.NET是一个事件驱动的设计环境,在下来的章节我们将会讲述一些事件的处理和方法

Page_Load

服务器控件在Page对象被加载,view state信息在此时也是可用的。Page_Load事件检测sSite是否为nothing并且把Session(“Site”)变量给它赋值。

Private Sub Page_Load(ByVal sender As System.Object, _

        ByVal e As System.EventArgs) Handles MyBase.Load

        If IsNothing(sSite) Then

            sSite = Session("Site")

        End If

    End Sub

srchbtn_Click

当搜索按钮被点击后搜索按钮的事件将被激活。这里我们编写代码来改变控件设置或在页面中显示文本。我们检测搜索是否包含文本然后调用SearchSite方法。DisplayContent()被调用来给web页面的不同控件赋值。

    '*********************************************************

    '

    ' srchbtn_Click 事件

    '

    ' 在此事件中增加代码.

    '

    '**********************************************************

    Private Sub srchbtn_Click(ByVal sender As System.Object, _

     ByVal e As System.EventArgs) Handles srchbtn.Click

        Dim strSearchWords As String

        '如果用户没有输入要搜索的词

        '则不执行文件搜索程序

        pnlSearchResults.Visible = False

        strSearchWords = Trim(Request.Params("search"))

        If Not strSearchWords.Equals("") Then

      Searchs.Site.ApplicationPath = String.Format("http://{0}{1}",

       Request.ServerVariables("HTTP_HOST"), Request.ApplicationPath)

            sSite = SearchSite(strSearchWords)

            Session("Site") = sSite

            dgrdPages.CurrentPageIndex = 0

            DisplayContent()

        End If

    End Sub

DisplayContent

DisplayContent()被调用来给web页面的不同控件赋值。通过调用BindDataGrid方法来设置DataGrid的内容。使用ViewState(SortExpression)来设置排序方法的描述。

    '*********************************************************************

    '

    ' DisplayContent 方法

    '

    ' 数据被绑定到各个地方

    '

    '*********************************************************************

    Private Sub DisplayContent()

        If Not IsNothing(sSite.PageDataset) Then

            pnlSearchResults.Visible = True

            lblSearchWords.Text = sSite.SearchWords

            If ViewState("SortExpression") Is Nothing Then

                ViewState("SortExpression") = "MatchCount Desc"

            End If

            BindDataGrid(ViewState("SortExpression"))

            lblTotalFiles.Text = sSite.TotalFilesSearched

            lblFilesFound.Text = sSite.TotalFilesFound

        End If

    End Sub

搜索

搜索的主要调用在这个方法中进行。UserSearch类保存所有搜索信息和搜索结果,之后我们将讲述这个类。UserSearch类对象也就是srchSite被创建,它的属性如SearchWordsSearchCriteria被赋值,并且srchSite.Search方法被调用。

    '************************************************************

    '

    ' SearchSite 方法

    '

    ' sSite.PageDataset 用来给dataset设置值。.

    '

    '************************************************************

   Private Function SearchSite(ByVal strSearch_

          As String) As Searchs.UserSearch

        Dim srchSite As Searchs.UserSearch

        srchSite = New Searchs.UserSearch()

        'Read in all the search words into one variable

        srchSite.SearchWords = strSearch

        If Phrase.Checked Then

            srchSite.SearchCriteria = Searchs.SearchCriteria.Phrase

        ElseIf AllWords.Checked Then

            srchSite.SearchCriteria = Searchs.SearchCriteria.AllWords

        ElseIf AnyWords.Checked Then

            srchSite.SearchCriteria = Searchs.SearchCriteria.AnyWords

        End If

        srchSite.Search(Server.MapPath("./"))

        Return srchSite

    End Function

DataGrid

DataGrid控件是代表一个多列的、完全模板化的表格,在众多的数据绑定控件中它的功能更加强大,而且DataGrid控件是ASP.NET为数据报表所特制的,因此我使用它来显示搜索结果。因为本文的重点是讲内部网络的搜索引擎,因此我只给出使用DataGrid特性的简单介绍。

数据绑定

数据绑定是从数据源中获取数据并且动态联结到可视化组件的属性的过程。因为DataGrid同时处理(只是在内存中有)更多的条目,你应该明确地把数据集合联结到DataGrid——那就是,数据源。

DataGrid的内容通过设置它的DataSource属性来实现。整个搜索结果被保存在sSite.PageDataset.Tables(“Pages”)中,因此DataGrid的内容被设置到dvwPages,即sSitePageDataset(“Pages”)DefaultView.BindDataGrid方法在每次页面加载的时候被调用。

    '************************************************************

    '

    ' BindDataGrid 方法

    '

    ' sSite.PageDataset 用来给dataset设置值

    '

    '************************************************************

    Private Sub BindDataGrid(ByVal strSortField As String)

        Dim dvwPages As DataView

        dvwPages = sSite.PageDataset.Tables("Pages").DefaultView

        dvwPages.Sort = strSortField

        dgrdPages.DataSource = dvwPages

        dgrdPages.DataBind()

    End Sub

控件可以根据数据域的结构动态创建列。“自动创建”是DataGrid的默认设置,但是你可以通过使用一个叫AutoGenerateColumnsBoolean属性来手动改变这个设置。当你需要控件只显示你在Columns集合中增加的列时,把这个属性设置成False即可;当你需要控件显示数据源要求的那么多列时,把这个属性设置成True(默认)即可。“自动创建”不能使用自定义表头文本,也不支持文本格式化。因此,这里我把它设置成False。你可以使用<asp:datagrid>服务器控件里的<columns>标签来绑定特定的列。

    <columns>

     <asp:TemplateColumn>

      <ItemTemplate>

       <%# DisplayTitle(Container.DataItem( "Title" ), _

            Container.DataItem( "Path" )) %>

       <br>

       <%# Container.DataItem( "Description" ) %>

       <br>

       <span class="Path">

        <%# String.Format("{0} - {1}kb", DisplayPath( _

             Container.DataItem( "Path" )) , _

             Container.DataItem( "Size" ))%>

       </span>

       <br>

       <br>

      </ItemTemplate>

     </asp:TemplateColumn>

    </columns>

DisplayTitle方法和DisplayPath方法用来把自定义信息显示在DataGrid的列中。

    '****************************************

    '

    ' DisplayTitle 方法

    '

    ' 显示搜索页的标题

    '

    '****************************************

    Protected Function DisplayTitle(ByVal Title _

        As String, ByVal Path As String) As String

      Return String.Format("<A href="{1}">{0}</A>", Title, Path)

    End Function

    '****************************************

    '

    ' DisplayPath 方法

    '

    ' 返回文件路径

    '

    '****************************************

    Protected Function DisplayPath(ByVal Path As String) As String

        Return String.Format("{0}{1}/{2}", _

         Request.ServerVariables("HTTP_HOST"), _

         Request.ApplicationPath, Path)

    End Function

分页

DataList控件不同的是,DataGrid控件支持数据分页显示,即,把数据源的行分别显示在多个页面中。数据源的大小很容易超过实际页面的区域,因此为了维持服务端的可扩展性和使页面对用户有更高的可访问性,你可以每次只显示某些行。为了实现DataGrid控件可分页功能,你可以通过设置控件的Allowpaging属性来告诉它。

分页栏是DataGrid控件提供的一个非常有趣和值得称赞的特性,它可以让用户轻易的从一个页面跳到另外一个页面。分页栏显示DataGrid控件的下面,它包含了已有页面的链接。当你点击这里的任何一个链接时,控件自动激活PageIndexChanged事件,并且更新相应的页面索引。当页面索引改变的时候dgrdPages_PageIndexChanged将被调用。

    '*****************************************************************

    '

    ' dgrdPages_PageIndexChanged 事件

    '

    ' CurrentPageIndex 被赋值为页面索引的值

    ' 然后datagrid 通过函数 BindDataGrid 被赋值

    '

    '*****************************************************************

    Protected Sub dgrdPages_PageIndexChanged(ByVal s As Object, _

      ByVal e As DataGridPageChangedEventArgs) _

      Handles dgrdPages.PageIndexChanged

        dgrdPages.CurrentPageIndex = e.NewPageIndex

        DisplayContent()

    End Sub

你可以通过PagerStyleMode属性控制分页栏,Mode属性的值从PagerMode中得到。这里我们选择逐条的数字按钮,每个按钮指向一个特定的页面。

<PagerStyle CssClass="GridPager" Mode="NumericPages"></PagerStyle>

排序

DataGrid控件实际上不支持行排序,但是只要数据源具有排序的功能,它就可以提供很好的排序支持。数据源根据用户在DataGrid控件的用户界面中选择的排序表达式返回排好序的记录。通过设置AllowSorting属性为True内置的排序机制就会被触发。

dgrdPages_SortCommand 被调用使DataGrid排序。SortCommand事件处理器通过DataGridSortCommandEventArgs类的SortExpression属性知道排序表达式。在我们的代码中,排序信息是一直存在的,因为它被保存在页面的ViewState集合中。

注:在我的页面中,我已经使表头无效,但是如果表头显示了,你可以通过它来使DataGrid排序。

    '*****************************************************************

    '

    ' dgrdAdditionalItems_SortCommand 事件

    '

    ' ViewState( "SortExpression" ) 被赋予

    ' 排序表达式的值

    ' 然后datagrid 通过调用函数 BindDataGrid.被赋值

    '

    '*****************************************************************

    Protected Sub dgrdPages_SortCommand(ByVal s As Object, _

        ByVal e As DataGridSortCommandEventArgs) _

        Handles dgrdPages.SortCommand

        ViewState("SortExpression") = e.SortExpression

        DisplayContent()

    End Sub

Page.vb

 

 

 

Page对象的作用是存储跟网站每个页面相关的数据。

Page类定义了以下属性

Path

存储文件路径

Title

存储HTMLTitle标签里的文本

Keywords

存储HTMLmeta keywords标签里的文本

Description

存储HTMLmeta description标签里的文本

Contents

存储HTML页面里面的文本

Matchcount

存储在HTML里找到的匹配数

    '***************************************************

    '

    ' Size 属性

    '

    ' 设置获取文件的大小

    '

    '***********************************************

    Public Property Size() As Decimal

        Get

            Return m_size

        End Get

        Set(ByVal Value As Decimal)

            m_size = Value

        End Set

    End Property

    '***************************************************

    '

    ' Path 属性

    '

    ' 设置获取文件的路径

    '

    '***********************************************

    Public Property Path() As String

        Get

            Return m_path

        End Get

        Set(ByVal Value As String)

            m_path = Value

        End Set

    End Property

    '***********************************************

    '

    ' Title 属性

    '

    '设置获取文件的标题

    '

    '***********************************************

    Public Property Title() As String

        Get

            Return m_title

        End Get

        Set(ByVal Value As String)

            m_title = Value

        End Set

    End Property

    '***********************************************

    '

    ' Keywords 属性

    '

    ' 设置获取文件中的 Keywords(Meta 标签中)

    '

    '***********************************************

    Public Property Keywords() As String

        Get

            Return m_keywords

        End Get

        Set(ByVal Value As String)

            m_keywords = Value

        End Set

    End Property

    '***********************************************

    '

    ' Description 属性

    '

    '设置获取文件中的description(meta tags )

    '

    '***********************************************

    Public Property Description() As String

        Get

            Return m_description

        End Get

        Set(ByVal Value As String)

            m_description = Value

        End Set

    End Property

    '***********************************************

    '

    ' Contents 属性

    '

    '设置获取文件的内容

    '

    '***********************************************

    Public Property Contents() As String

        Get

            Return m_contents

        End Get

        Set(ByVal Value As String)

            m_contents = Value

        End Set

    End Property

    '***********************************************

    '

    ' MatchCount 属性

    '

    '设置获取文件的匹配数

    '

    '***********************************************

    Public Property MatchCount() As Integer

        Get

            Return m_matchcount

        End Get

        Set(ByVal Value As Integer)

            m_matchcount = Value

        End Set

    End Property

Page类有两个私有函数和两个公有函数,定义如下:

CheckFileInfo方法

这是个公有方法,它检测titledescriptioncontent是否存在,如果title的文本为空,则给它赋予默认值No Title;如果description的文本为空则要么把content的值给它要么给它赋There is no description available for this page

    '*************************************************

    '

    ' CheckFileInfo 方法

    '

    '子程序,检测文件是否包含

    ' title decription

    '

    '*************************************************

    Public Sub CheckFileInfo()

        '如果页面不包含title

        ' 赋予Title变量适当的显示信息

        If IsNothing(m_title) Or m_title.Trim().Equals("") Then

            m_title = "No Title"

        End If

        '如果页面不包含title(description)

        '赋予description变量适当的显示信息

        If IsNothing(m_description) Or _

          m_description.Trim().Equals("") Then

            If IsNothing(m_contents) Or _

             m_contents.Trim().Equals("") Then

                m_description = _

                 "There is no description available for this page"

            Else

                If m_contents.Length > 200 Then

                    m_description = m_contents.Substring(0, 200)

                Else

                    m_description = m_contents

                End If

            End If

        End If

    End Sub

Search方法

Search方法是一个公有方法,它根据搜索条件决定调用SearchPhrase方法还是调用SearchWordsSearchPhrase方法搜索词组而SearchWords搜索所有或任何单词。这两个方法都调用SearchPattern方法,这个方法使用正则表达式来搜索文件。

    '*******************************************

    '

    ' Search 方法

    '

    ' 搜索文件子程序

    '

    '*******************************************

    Public Sub Search(ByVal strSearchWords As String, _

          ByVal SrchCriteria As SearchCriteria)

        '如果用户选择搜索词组

        If SrchCriteria = SearchCriteria.Phrase Then

            SearchPhrase(strSearchWords)

            '如果是搜索所有或任何单词

        Else

            SearchWords(strSearchWords, SrchCriteria)

        End If

    End Sub

    '******************************************************

    '

    ' SearchPhrase 方法

    '

    ' 搜索文件子程序

    '

    '******************************************************

    Private Sub SearchPhrase(ByVal strSearchWords As String)

        Dim mtches As MatchCollection

        mtches = SearchPattern(strSearchWords)

        '检查词组是否被找到

        If mtches.Count > 0 Then

            '获取词组被找到的次数

            m_matchcount = mtches.Count

        End If

    End Sub

    '**************************************************

    '

    ' SearchWords 方法

    '

    ' 搜索文件子程序

    '

    '**************************************************

    Private Sub SearchWords(ByVal strSearchWords As String, _

       ByVal SrchCriteria As SearchCriteria)

        Dim intSearchLoopCounter As Integer

        Dim sarySearchWord As String()

        '包含被搜索的所有单词的数组

        Dim mtches As MatchCollection

        '分开每个被搜索的单词并放到一个数组中

        sarySearchWord = Split(Trim(strSearchWords), " ")

        '循环搜索每个要被搜索的单词

        For intSearchLoopCounter = 0 To UBound(sarySearchWord)

            '设置搜索的方式

        mtches = SearchPattern(sarySearchWord(_

        intSearchLoopCounter))

            If SrchCriteria = SearchCriteria.AnyWords Then

                m_matchcount = m_matchcount + mtches.Count

            ElseIf SrchCriteria = SearchCriteria.AllWords Then

                '检查是否有单词不搜索到

                If mtches.Count > 0 Then

                    '获取搜索单词匹配的次数

                    If m_matchcount = 0 Or (m_matchcount > 0 _

                        And m_matchcount > mtches.Count) Then

                        m_matchcount = mtches.Count

                    End If

                Else

                    '如果被搜索的单词没有找到,则设置

                    '搜索被找到变量为false,因为

                    '其中的一个单词没有被找到

                    m_matchcount = 0

                    Exit Sub

                End If

            End If

        Next

    End Sub

转义符/b是一个特殊的情况。在正则表达式中,除了在字符“[”和“]”之间表示退格符,/b表示一个单词的边界(在/w/W之间)。在替换模式中,/b总是表示退格。

当我们不是使用UTF-8编码的时候必须把单词边界去掉。

    '****************************************************

    '

    ' SearchPattern 方法

    '

    ' 搜索文件子程序

    '

    '****************************************************

    Private Function SearchPattern( _

       ByVal strSearchWord As String) As MatchCollection

        Dim regexp As Regex

        Dim strPattern

        'Set the pattern to search for

        regexp = New Regex("", RegexOptions.IgnoreCase)

        'Search the file for the phrase

        If Searchs.Site.Encoding.Equals("utf-8") Then

            strPattern = "/b{0}/b"

        Else

            strPattern = "{0}"

        End If

        Return regexp.Matches(m_contents, String.Format(strPattern, _

         strSearchWord), RegexOptions.IgnoreCase)

    End Function

UserSearch.vb

 

它包含以下属性:

SearchCriteria

保存、获取用户搜索选项

SearchWords

保存、获取用户搜索的单词

TotalFilesSearched

获取搜索到的总文件

TotalFilesFound

获取找到的总文件

    '**********************************************************

    '

    ' SearchCriteria 属性

    '

    ' 设置、获取网站搜索条件    '

    '**********************************************************

    Public Property SearchCriteria() As Searchs.SearchCriteria

        Get

            Return m_searchCriteria

        End Get

        Set(ByVal Value As Searchs.SearchCriteria)

            m_searchCriteria = Value

        End Set

    End Property

    '**********************************************************

    '

    ' SearchWords 属性

    '

    '设置、获取网站搜索单词

    '

    '**********************************************************

    Public Property SearchWords() As String

        Get

            Return m_searchWords

        End Get

        Set(ByVal Value As String)

            m_searchWords = Value

        End Set

    End Property

    '**********************************************************

    '

    ' TotalFilesSearched 属性

    '

    '获取搜索到的总文件数

    '

    '**********************************************************

    Public ReadOnly Property TotalFilesSearched() As Integer

        Get

            Return m_totalFilesSearched

        End Get

    End Property

    '**********************************************************

    '

    ' TotalFilesFound 属性

    '

    ' 获取网站文件总数

    '

    '**********************************************************

    Public ReadOnly Property TotalFilesFound() As Integer

        Get

            Return m_totalFilesFound

        End Get

    End Property

    '**********************************************************

    '

    ' PageDataset 共享属性

    '

    ' 获取整个网站的数据

    '

    '**********************************************************

    Public ReadOnly Property PageDataset() As DataSet

        Get

            Return m_dstPages

        End Get

    End Property

Search方法

搜索的实际处理从这里开始。存储搜索结果的DataSet在此创建,ProcessDirectory方法被调用。

    '********************************************

    '

    ' Search 方法

    '

    ' 搜索整个网站

    '

    '********************************************

    Public Function Search(ByVal targetDirectory As String) As DataSet

        '如果网站是英文的则调用 Server.HtmlEncode 方法

        If Searchs.Site.EnglishLanguage = True Then

            '在同一个字符串中用HTML编码代替所有的HTML标签

            ' (停止用户输入HTML标签)

            m_searchWords = m_page.Server.HtmlEncode(m_searchWords)

            '如果网站不是英文的,则改变脚本标签

        Else

            '用“<”和“>”的HTML标签替换标签“<”和“>

            m_searchWords = Replace(m_searchWords, "<", "<", 1, -1, 1)

            m_searchWords = Replace(m_searchWords, ">", ">", 1, -1, 1)

        End If

        If m_dstPages Is Nothing Then

            m_dstPages = Searchs.PagesDataset.Create()

        End If

        ProcessDirectory(targetDirectory)

        Return m_dstPages

    End Function

ProcessDirectory方法

ProcessDirectory方法遍历所有文件并调用ProcessFile方法。然后,变量搜有子目录并调用自己。

    '*********************************************

    '

    ' ProcessDirectory 方法

    '

    ' 搜索目录中的文件

    '

    '********************************************

    Private Sub ProcessDirectory(ByVal targetDirectory As String)

        Dim fileEntries As String()

        Dim subdirectoryEntries As String()

        Dim filePath As String

        Dim subdirectory As String

        fileEntries = Directory.GetFiles(targetDirectory)

        ' 处理目录中找到的文件列表

        For Each filePath In fileEntries

            m_totalFilesSearched += 1

            ProcessFile(filePath)

        Next filePath

        subdirectoryEntries = Directory.GetDirectories(targetDirectory)

        ' 递规进入目录的子目录

        For Each subdirectory In subdirectoryEntries

            '检测确认要被搜索的文件夹不是被禁用的文件夹

            '如果是则不搜索

            If Not InStr(1, Searchs.Site.BarredFolders, _

             Path.GetFileName(subdirectory), vbTextCompare) > 0 Then

                '调用搜索子程序搜索网站

                ProcessDirectory(subdirectory)

            End If

        Next subdirectory

    End Sub 'ProcessDirectory

ProcessFile方法

ProcessFile调用GetInfoGetInfo函数返回包含特定文件所有信息的Searchs.Page对象。然后,它检测matchcount是否大于0并调用CheckFileInfo函数清理保存在Page对象中的信息,然后把文件保存在PagesDataset中。

    '*******************************************************

    '

    ' ProcessFile 函数

    '

    ' 处理已找到的文件真实过程在此开始

    '

    '*******************************************************

    Private Sub ProcessFile(ByVal FPath As String)

        Dim srchFile As Searchs.Page

        srchFile = GetInfo(FPath)

        If Not IsNothing(srchFile) Then

            srchFile.Search(m_searchWords, m_searchCriteria)

            If srchFile.MatchCount > 0 Then

                m_totalFilesFound += 1

                'Response.Write(srchFile.Contents)

                srchFile.CheckFileInfo()

                Searchs.PagesDataset.StoreFile(m_dstPages, srchFile)

            End If

        End If

    End Sub 'ProcessFile

GetInfo方法

GetInfo方法的主要任务是获取文件的数据。它调用共享(静态)函数Searchs.FileContent.GetFileInfo大多数的工作在此完成。

    '*****************************************************************

    '

    ' GetInfo 方法

    '

    ' 文件信息在此方法被获取

    '

    '*****************************************************************

    Private Function GetInfo(ByVal FPath As String) As Searchs.Page

        Dim fileInform As New FileInfo(FPath)

        Dim sr As StreamReader

        Dim srchFile As New Searchs.Page()

        Dim strBldFile As New StringBuilder()

        Dim strFileURL As String '保存网站中文件路径

        '检测文件扩展名,确认文件

        '是要被搜索的后缀的文件

        If InStr(1, Searchs.Site.FilesTypesToSearch, _

         fileInform.Extension, vbTextCompare) > 0 Then

            'm_page.Trace.Warn("File ext.", fileInform.Extension)

            '确认要被搜索的文件不是被禁止的文件

            '如果是,则不搜索

            If Not InStr(1, Searchs.Site.BarredFiles, _

             Path.GetFileName(FPath), vbTextCompare) > 0 Then

                'm_page.Trace.Warn("File", FPath)

                If Not File.Exists(FPath) Then

                    'm_page.Trace.Warn("Error", _

                    'String.Format("{0} does not exist.", FPath))

                    '在此增加抛出异常

                    '

                    '

                    Return Nothing

                End If

                Searchs.FileContent.GetFileInfo(FPath, srchFile)

                Return srchFile

            End If

        End If

        Return Nothing

    End Function

 

FileContent.vb

 

这里页面中的大量数据被检索出来。如果文件是静态文件,则文件内容通过使用GetStaticFileContent方法被读取出来;如果文件是动态的,则文件内容通过使用GetDynamicFileContent从服务器检索出来。title的信息从title标签中检索出来,meta标签中的descritpionkeywords通过调用GetMetaContent方法检索出来。通过调用Searchs.CleanHtml.Clean方法文件内容从HTML页面中被抽取出来。

    '**********************************************

    '

    ' GetFileInfo 方法

    '

    ' 文件信息在此方法中获取

    '

    '**********************************************

    Public Shared Sub GetFileInfo(ByVal FPath As String, _

         ByVal srchFile As Searchs.Page)

        Dim fileInform As New FileInfo(FPath)

        Dim strBldFile As New StringBuilder()

        Dim fileSize As Decimal = fileInform.Length / 1024

        srchFile.Size = fileSize

        GetFilePath(FPath, srchFile)

        If InStr(1, Searchs.Site.DynamicFilesTypesToSearch, _

          fileInform.Extension, vbTextCompare) > 0 Then

            m_page.Trace.Warn("Path", String.Format("{0}/{1}", "", _

             srchFile.Path))

            GetDynamicFileContent(srchFile)

        Else

            GetStaticFileContent(FPath, srchFile)

        End If

        If Not srchFile.Contents.Equals("") Then

            srchFile.Contents = sr.ReadToEnd()

            '读取文件中title之间的内容

        srchFile.Title = GetMetaContent(srchFile.Contents,_

             "<title>", "</title>")

            'm_page.Trace.Warn("Page Title", strPageTitle)

            '读取文件中meta标签的description

        srchFile.Description = GetMetaContent(srchFile.Contents,_

           "<meta name=""description"" content=""", ",""">")

            'm_page.Trace.Warn("Page Desc", strPageDescription)

            '读取文件中meta标签的keywords

        srchFile.Keywords = GetMetaContent(srchFile.Contents,_

          "<meta name=""keywords"" content=""", ",""">")

            'm_page.Trace.Warn("Page Keywords", strPageKeywords)

            srchFile.Contents = _

              Searchs.CleanHtml.Clean(srchFile.Contents)

            srchFile.Contents = _

              strBldFile.AppendFormat("{0} {1} {2} {3}", _

              srchFile.Contents, srchFile.Description, _

              srchFile.Keywords, srchFile.Title).ToString.Trim()

            'm_page.Trace.Warn("File Info", strBldFile.ToString)

        End If

    End Sub

    '******************************************************

    '

    ' GetStaticFileContent 方法

    '

    ' 文件内容在此方法中被获取

    '

    '*******************************************************

    Private Shared Sub GetStaticFileContent(_

        ByVal FPath As String, ByVal srchFile As Searchs.Page)

        Dim sr As StreamReader

        If Searchs.Site.Encoding.Equals("utf-8") Then

            sr = File.OpenText(FPath)

        Else

            sr = New StreamReader(FPath, _

    Encoding.GetEncoding(Searchs.Site.Encoding))

        End If

        Try

            srchFile.Contents = sr.ReadToEnd()

            sr.Close()

        Catch ex As Exception

            m_page.Trace.Warn("Error", ex.Message)

            srchFile.Contents = ex.Message

        End Try

    End Sub

GetDynamicFileContent

GetDynamicFileContent根据编码调用两个分支函数GetDynamicFileContentOtherGetDynamicFileContentUTF总的一个。

    '*********************************************************************

    '

    ' GetDynamicFileContent Method

    '

    ' 在此方法中文件内容被获取

    '

    '*********************************************************************

    Private Shared Sub GetDynamicFileContent(ByVal srchFile As Searchs.Page)

        Dim wcMicrosoft As System.Net.WebClient

        If Searchs.Site.Encoding.Equals("utf-8") Then

            GetDynamicFileContentUTF(srchFile)

        Else

            GetDynamicFileContentOther(srchFile)

        End If

    End Sub

System.Net.WebClient提供公共方法根据指定的URI资源发送或接收数据。我们使用DownloadData来从资源那里下载数据并返回字节数组。

应用程序使用encoding把原始的编码方式(Unicode)映射为其他编码方式来实现公共语言运行时;使用decoding把非原始编码方式(非Unicode)映射为原始编码方式。System.Text命名空间提供了类允许你对字符进行编码和解码。


 

    '****************************************************************

    '

    ' GetDynamicFileContentOther 方法

    '

    ' 按照提供的编码方式,文件内容在此方法中被获取

'

'

    '****************************************************************

    Private Shared Sub GetDynamicFileContentOther( _

           ByVal srchFile As Searchs.Page)

        Dim wcMicrosoft As System.Net.WebClient

        Dim fileEncoding As System.Text.Encoding

        Try

        fileEncoding = System.Text.Encoding.GetEncoding(_

       Searchs.Site.Encoding)

            srchFile.Contents = fileEncoding.GetString( _

            wcMicrosoft.DownloadData(String.Format("{0}/{1}", _

          Searchs.Site.ApplicationPath, srchFile.Path)))

        Catch ex As System.Net.WebException

            m_page.Trace.Warn("Error", ex.Message)

            srchFile.Contents = ex.Message

        Catch ex As System.Exception

            m_page.Trace.Warn("Error", ex.Message)

            srchFile.Contents = ex.Message

        End Try

    End Sub

UTF8Encoding类使用通用字符集转换格式——8位格式(UTF-8)对Unicode字符进行编码。这种编码支持所有Unicode字符值和代理字符。

    '*********************************************************************

    '

    ' GetDynamicFileContentUTF 方法

    '

    ' 根据utf-8编码方式文件内容被获取

    '

    '*********************************************************************

    Private Shared Sub GetDynamicFileContentUTF( _

           ByVal srchFile As Searchs.Page)

        Dim wcMicrosoft As System.Net.WebClient

        Dim objUTF8Encoding As UTF8Encoding

        Try

            wcMicrosoft = New System.Net.WebClient()

            objUTF8Encoding = New UTF8Encoding()

            srchFile.Contents = objUTF8Encoding.GetString( _

            wcMicrosoft.DownloadData(String.Format("{0}/{1}", _

         Searchs.Site.ApplicationPath, srchFile.Path)))

        Catch ex As System.Net.WebException

            m_page.Trace.Warn("Error", ex.Message)

            srchFile.Contents = ex.Message

        Catch ex As System.Exception

            m_page.Trace.Warn("Error", ex.Message)

            srchFile.Contents = ex.Message

        End Try

    End Sub

GetFilePath方法

GetFilePath方法把本地文件夹路径转换成网站的URL

    '*****************************************

    '

    ' GetFilePath 方法

    '

    ' 在此方法中文件路径被转换成用于显示的超链接

    '

    '

    '*****************************************

    Private Shared Sub GetFilePath(ByVal strFileURL As String,_

                ByVal srchFile As Searchs.Page)

        '把服务器文件路径转换成URL文件路径

        strFileURL = Replace(strFileURL, m_page.Server.MapPath("./"), "")

        '把文件URL中,NT使用的反斜杠替换成

        '互联网使用的斜杠

        strFileURL = Replace(strFileURL, "/", "/")

        '把文件名和路径编码成URL的编码方法

        strFileURL = m_page.Server.UrlEncode(strFileURL)

        '为了防止编码后出现反斜杠(斜杠)

        strFileURL = Replace(strFileURL.Trim(), _

                 "% 2f", "/", vbTextCompare)

        srchFile.Path = strFileURL

        m_page.Trace.Warn("Url", srchFile.Path)

    End Sub

GetMetaContent方法

GetMetaContent方法使用正则表达式剔除标签并获取需要的信息。

    '************************************************

    '

    ' GetMetaContent 方法

    '

    ' Meta的内容在此函数中被剥离

    '

    '************************************************

    Private Shared Function GetMetaContent(ByVal strFile As String, _

     ByVal strMetaStart As String, ByVal strMetaEnd As String) As String

        '列出在title标签之间的文本

        Dim regexp As Regex

        Dim strMeta As String

        Dim strPattern As String

        Dim strInPattern As String

        '如果在meta标签中 description keywords 没有找到 ,则

        '使用http-equiv= 代替 name=

        If InStr(1, LCase(strFile), strMetaStart, 1) = 0 _

         And InStr(strMetaStart, "name=") Then

            ' name= 替换成 http-equiv=

            strMetaStart = Replace(strMetaStart, "name=", "http-equiv=")

        End If

        '建立模式

        strInPattern = "((.|/n)*?)"

        strPattern = String.Format("{0}{1}{2}", _

         strMetaStart, strInPattern, strMetaEnd)

        regexp = New Regex(strPattern, RegexOptions.IgnoreCase)

        '匹配模式

        strMeta = regexp.Match(strFile).ToString

        '建立模式

        strInPattern = "(.*?)"

        strPattern = String.Format("{0}{1}{2}", _

         strMetaStart, strInPattern, strMetaEnd)

        '获取模式内容

      strMeta = regexp.Replace(strMeta, strPattern,_

                "$1", RegexOptions.IgnoreCase)

        Return strMeta

    End Function

PagesDataset.vb

 

这个类用来创建DataSet,它有两个方法组成CreateStoreFileCreate方法创建存储搜索结果的DataSetStoreFile负责把记录添加到DataSetDataTable中。

 

    '*******************************************************

    '

    ' Create 方法 共享(静态)方法

    '

    ' 为页面创建dataset并返回结果

    '

    '********************************************************

    Public Shared Function Create() As DataSet

        '对象被定义

        Dim pgDataSet As New DataSet()

        Dim keys(1) As DataColumn

        '表被创建并加入Table集合

        pgDataSet.Tables.Add(New DataTable("Pages"))

        '表的结构被定义

        pgDataSet.Tables("Pages").Columns.Add("PageId", _

           System.Type.GetType("System.Int32"))

      pgDataSet.Tables("Pages").Columns.Add("Title",_

         System.Type.GetType("System.String"))

        pgDataSet.Tables("Pages").Columns.Add("Description", _

           System.Type.GetType("System.String"))

        pgDataSet.Tables("Pages").Columns.Add("Path", _

           System.Type.GetType("System.String"))

        pgDataSet.Tables("Pages").Columns.Add("MatchCount", _

           System.Type.GetType("System.Int32"))

        pgDataSet.Tables("Pages").Columns.Add("Size", _

     System.Type.GetType("System.Decimal"))

        'PageId 被定义为标识

        pgDataSet.Tables("Pages").Columns("PageID").AutoIncrement = True

        pgDataSet.Tables("Pages").Columns("PageID").AutoIncrementSeed = 1

        'PageId 被定义为主键

        keys(0) = pgDataSet.Tables("Pages").Columns("PageId")

        pgDataSet.Tables("Pages").PrimaryKey = keys

        Return pgDataSet

    End Function

    '********************************************************

    '

    ' StoreFile 方法 共享(静态)方法

    '

    '为页面创建dataset并返回结果

    '

    '********************************************************

    Public Shared Sub StoreFile(ByVal dstPgs As DataSet,_

                  ByVal srchPg As Searchs.Page)

        '对象被定义

        Dim pageRow As DataRow

        '新行被创建

        pageRow = dstPgs.Tables("Pages").NewRow()

        '数据被设置

        pageRow("Title") = srchPg.Title

        pageRow("Description") = srchPg.Description

        pageRow("Path") = srchPg.Path

        pageRow("MatchCount") = srchPg.MatchCount

        pageRow("Size") = srchPg.Size

        '行被加入dataset

        dstPgs.Tables("Pages").Rows.Add(pageRow)

    End Sub

CleanHtml.vb

 

 

 

CleanHtml类包含单独一个共享(静态)函数,这个函数使用正则表达式清空HTML内容。

    '*****************************************************

    '

    ' CleanFileContent 方法

    '

    ' 清空html文件内容的子程序

    '

    '*****************************************************

    Public Shared Function Clean(ByVal Contents As String) As String

        Dim regexp As Regex

        Dim strPattern As String

        strPattern = ""

        regexp = New Regex(strPattern, RegexOptions.IgnoreCase)

      Contents = regexp.Replace(Contents, _

       "<(select|option|script|style|title)(.*?)" & _

       ">((.|/n)*?)</(SELECT|OPTION|SCRIPT|STYLE|TITLE)>",_

       " ", RegexOptions.IgnoreCase)

        Contents = regexp.Replace(Contents, "&(nbsp|quot|copy);", "")

        'Contents = regexp.Replace(Contents, "<[^>]*>", "")

      Contents = regexp.Replace(Contents, "<([/s/S])+?>",_

        " ", RegexOptions.IgnoreCase).Replace(" ", " ")

        'Contents = regexp.Replace(Contents, "<[^<>]+>",_

        " ", RegexOptions.IgnoreCase)

        'Contents = regexp.Replace("(<(/w+)[^>]*?>(.*?)<//1>", "$1")

        Contents = regexp.Replace(Contents, "/W", " ")

        'Trace.Warn("File Contents", Contents)

        Return Contents

    End Function

Site.vb

 

 

Site类由保存整个网站配置信息的共享(静态)属性组成。这些属性使用ConfigurationSettings.AppSettingsweb.config文件获取值。

以下是Site类的属性:

 

FilesTypesToSearch

返回要搜索的文件类型

DynamicFilesTypesToSearch

返回要搜索的动态文件

BarredFolders

返回禁止的文件夹

EnglishLanguage

返回一个Boolean值,表示语言是否为英语

Encoding

返回网站编码

BarredFiles

返回禁止的文件

ApplicationPath

设置或返回应用程序路径

    '*************************************************

    '

    ' FilesTypesToSearch 只读属性

    '

    ' 获取网站中要搜索的文件类型

    '

    '*************************************************

    Public Shared ReadOnly Property FilesTypesToSearch() As String

        Get

        Return ConfigurationSettings.AppSettings(

        "FilesTypesToSearch")

        End Get

    End Property

    '*************************************************

    '

    ' DynamicFilesTypesToSearch 只读属性

    '

    ' 获取网站中要搜索的动态文件

    '

    '*************************************************

    Public Shared ReadOnly Property DynamicFilesTypesToSearch() As String

        Get

        Return ConfigurationSettings.AppSettings(_

         "DynamicFilesTypesToSearch")

        End Get

    End Property

    '*************************************************

    '

    ' BarredFolders 只读属性

    '

    ' 获取网站中禁止的文件夹

    '

    '*************************************************

    Public Shared ReadOnly Property BarredFolders() As String

        Get

            Return ConfigurationSettings.AppSettings("BarredFolders")

        End Get

    End Property

    '*************************************************

    '

    ' BarredFiles 只读属性

    '

    ' 获取网站中禁止的文件

    '

    '*************************************************

    Public Shared ReadOnly Property BarredFiles() As String

        Get

            Return ConfigurationSettings.AppSettings("BarredFiles")

        End Get

    End Property

    '*************************************************

    '

    ' EnglishLanguage 属性

    '

    ' 获取语言是否为英语

    '

    '*************************************************

    Public Shared ReadOnly Property EnglishLanguage() As String

        Get

            Return ConfigurationSettings.AppSettings("EnglishLanguage")

        End Get

    End Property

    '*********************************************************************

    '

    ' Encoding 属性

    '

    ' 获取网站的编码

    '

    '*********************************************************************

    Public Shared ReadOnly Property Encoding() As String

        Get

            Return ConfigurationSettings.AppSettings("Encoding")

        End Get

    End Property

    '**********************************************************

    '

    ' ApplicationPath 属性

    '

    ' 设置或获取应用程序路径

    '

    '**********************************************************

    Public Property ApplicationPath() As String

        Get

            Return m_ApplicationPath

        End Get

        Set(ByVal Value As String)

            m_ApplicationPath = Value

        End Set

    End Property

Web.Config

ASP.NET配置系统建立一个可扩展的基本机构,允许你在你的ASP.NET应用程序部署的时候定义配置设置,因此你可以在任何时候增加或修改配置设置,这使对Web应用程序和服务器的操作影响降到最小。多个以Web.Config命名的配置文件可以出现在一个ASP.NET Web应用程序服务器的多个文件夹里面。每个Web.Config文件应用配置设置到它所在的文件夹和它所在的子文件夹。正如前面所述,网站的配置可以设置在web.config文件中。

  <appSettings>

   <!在下面行中放置你要搜索的文件类型的名称,以逗号分隔 -->

    <add key="FilesTypesToSearch" value=".htm,.html,.asp,.shtml,.aspx" />

   <!--在下面行中放置你要搜索的动态文件类型的名称,以逗号分隔-->

    <add key="DynamicFilesTypesToSearch" value=".asp,.shtml,.aspx" />

   <!--在下面行中放置你不要搜索的文件夹名称,以逗号分隔-->

    <add key="BarredFolders"

     value="aspnet_client,_private,_vti_cnf,_vti_log,_vti_pvt,

_vti_script,_vti_txt,cgi_bin,_bin,bin,_notes,images,scripts"

 />

   <!--在下面行中放置你不要搜索的文件名称(包括文件扩展名),以逗号分隔-->

    <add key="BarredFiles"

     value="localstart.asp,iisstart.asp,AssemblyInfo.vb,

          Global.asax,Global.asax.vb,SiteSearch.aspx"

 />

   <!如果你的网站语言不是英语的,把这个boolean值设置为False-->

    <add key="EnglishLanguage" value="True" />

   <!把这个值设置成网站编码-->

    <add key="Encoding" value="utf-8" />

  </appSettings>

如何整合

应用程序已经通过根目录中的web窗体页面SiteSearch.aspx测试,所以我的建议是你使用同样的方式测试。然后,你可以尝试把它移到任何(网站)子目录。我的所有类已经放到components文件夹,你可以把它放到你的任何文件夹。

注意:

1.对于那些没有Visual Studio.Net的用户

       1.从超链接“下载最新版本的示例工程(Visual Studio .NET 非必须的)”下载

       2.把SearchDotnet.dll放在根目录的bin文件夹里

       3.把SiteSearch.aspxweb.config放在根目录

2.使用XML版本

       1.从超链接“下载读写XML示例工程 (VB.NET)

       2.项目包含以下文件:

              aAdminiSearch.aspx用来把xml写入文件。

              bSiteSearch.aspx用来搜索文件。

              c.我的所有类已经放入components文件夹。

错误

当应用程序被放到根目录的时候,你可能会得到以下错误。“The remote server returned an error: (401) Unauthorized.”或者“The remote server returned an error: (500) Internal Server Error.

造成这个错误的原因是:

1.如果服务器返回“(401) Unauthorized”,应用程序由于访问权限不能读取文件。

2.如果服务器返回“(500) Internal Server Error”,正在尝试读取的页面返回一个错误,应用程序正在尝试读取的页面因为出现一个错误或者需要参数而返回错误。

按照以下步骤修改错误

1.在web.config中确认BarredFolders列表包含“aspnet_client,_private,_vti_cnf, _vti_log,_vti_pvt,_vti_script,_vti_txt, cgi_bin,_bin,bin,_notes,images,scripts

2.确认BarredFiles列表是否全面,并且包含“localstart.asp,iisstart.asp

全球化

搜索引擎模块可以轻易被全球化。以此为目的,我们来看看怎样把它转换成汉语语言。

Web.config

XML声明必须出现在文档任何内容(包括空格),在“<”开始之前的第一行。

在文档中XML声明由以下组成“<?xml version="1.0" encoding="Your Encoding" ?>”,默认情况下,visual studio使用utf-8编码,这需要改变成你要使用的编码。这里我们把它改成gb2312。因此XML声明需要被修改成以下所示:

英语

<?xml version="1.0" encoding="utf-8" ?>

汉语

<?xml version="1.0" encoding="gb2312" ?>

requestEncodingresponseEncoding指定请求和响应的可能编码。Machine.config.NETFramework安装的时候创建,包含在Machine.config文件的<globalization>标签中默认编码是UTF-8。如果编码没有在Manchine.configWeb.config文件指定,编码默认按照电脑的区域选项的本地设置来指定。我们需要更改requestEncodingresponseEncoding来反映编码的改变。

英语

<globalization requestEncoding="utf-8" responseEncoding="utf-8" />

汉语

<globalization requestEncoding="gb2312" responseEncoding="gb2312" />

为了避免当编码改变的时候生成代码,我们需要在appsettings增加encoding设置。

         <!把网站的编码设置在这里-->

    <add key="Encoding" value="gb2312" />

并把EnglishLanguage键设为False

   <!如果你的网站语言不是英语的,把这个boolean值设置为False-->

    <add key="EnglishLanguage" value="False" />

SiteSearch.aspx

最后同样重要的是,codepage属性必须增加在页面的指令中。

英语

<%@ Page Language="vb" Trace="False" AutoEventWireup="false" Codebehind="SiteSearch.aspx.vb" Inherits="SearchDotnet.SiteSearch" debug="false" %>

汉语

<%@ Page Language="vb" Trace="False" AutoEventWireup="false" Codebehind="SiteSearch.aspx.vb" Inherits="SearchDotnet.SiteSearch" debug="false" codePage="936" %>

强化代码

此应用程序是为小型站点编写的,对于更大的站点,代码可以进一步加强。事实上你可能需要周期性地写入到数据库(比如XML文件),然后从那里读取。我将为此提供一些提示。(我已经包含进一个“读写XML示例工程”)

1)在我的代码中,我使用正则表达式来搜索并过滤数据,不然你将要通过以下方法把全部数据(非过滤的数据)写入XML文件。

    Private Shared Sub WriteXmlToFile(ByVal thisDataSet As DataSet)

        If thisDataSet Is Nothing Then

            Return

        End If

        thisDataSet.WriteXml(XMLFile)

    End Sub

2)然后你将需要读取xml文件并保存到dataset Searchs.Site.PageDataset.Tables("Pages").

    Private Shared Function ReadXmlFromFile() As DataSet

        ' 创建 DataSet.

        Dim newDataSet As New DataSet("New DataSet")

        ' 读取后台 XML 文档

        ' 创建读取文件的文件流.

      Dim fsReadXml As New System.IO.FileStream(XMLFile,

        System.IO.FileMode.Open)

        ' 创建一个 XmlTextReader 来读取文件.

        Dim myXmlReader As New System.Xml.XmlTextReader(fsReadXml)

        ' XML文档读入DataSet.

        newDataSet.ReadXml(myXmlReader)

        ' 关闭 XmlTextReader

        myXmlReader.Close()

        Return newDataSet

    End Function

3)每次搜索,你将要使用PageDataset.TablesSelect方法来筛选搜索结果,筛选dataset正是如此。FillDataset方法包含创建添加搜索结果(DataRow数组)到数据库的逻辑。

    Private Sub FiterPagesDatset()

        Dim strExpr As String

        Dim foundRows As DataRow()

      Dim Field() As String = {

       "Title", "Description", "Keywords", "Contents"}

        strExpr = SomeFunction '你的建立查询的功能.

      foundRows = Searchs.Site.PageDataset.Tables(

        "Pages").Select(strExpr)

        FillDataset(foundRows)

    End Sub

4)筛选的结果被保存在另外一个dataset中,并用他来显示结果。

有趣的地方

当我在做这个项目的时候,我的问题是怎样显示结果。DataGrid是我的选择,因为我们可以开发出很多其他列表控件所没有的特性。一旦我的问题得到解决,下一步就是怎样传递内容到DataGridDataSet是唯一可选的。在保存信息到DataSet之前,当我进一步处理时,我不得不把页面的大量信息移来移去。我决定使用Site对象来保存信息。

作者之一建议下面的最优方法:

a类应该要小到大约只有几个方法和属性。

b.方法应该短小到大约只有几行。

经过仔细的分析代码和保持最优方法在心里,我重新设计代码成现在的样子。

历史

l         修改代码可以读取动态页面。

l         增强代码支持全球化。

l         我已经加入一个读写XML的示例工程

许可

本文没有附加明确的许可,但是在文章正文或者下载文件中可能包含使用条款,如果有疑问请通过下面讨论区和作者联系。

可能使用的许可作者列表可以在这里找到。

关于作者

 

Stevan Rodrigues

 

Stevan 微软认证.NET架构解决方案开发专家(早期MCSD.Net获得者世界范围内前2500之一), 微软认证.NET应用程序开发专家 – MCAD.Net (特许会员 世界范围内前5000名的开发工程师之一).
他是理科硕士,主修数学,在阿拉伯联合酋长国的Dubai从事IT业已经有一段时间。最近他在印度作为一名技术架构师。

职业:

架构师

地点:

印度

其他有名的应用程序和工具文章:

A hierarchical task manager with native XML support for custom reporting.

A memory leak detector for Visual C++ packaged in an easy to use library!

KeePass is a free, open-source, light-weight and easy-to-use password safe.

This tool automatically converts Visual C++ 7.0 projects back to Visual C++ 6.0 projects.

A drag & drop ImageList and ToolBar generator . Simplifies creation of large image lists and supports effects .

 

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值