一个完备的微型正则表达式【源码实现】

说明:刚才发现在处理*元字符时弄错了,代码修改重新上传到CSDN了,文章中的示例代码也进行了修改。

      前一版本有错误的代码中将*处理成了前一字符至少出现1次,修改后为出现0次或多次。

      如果你是通过CSDN下载找到这个页面的,请务必留意,你下载的可能不是最终版的代码。最终版代码下载地址:

      http://download.csdn.net/detail/sun2043430/5333836


看了《代码之美》第一章的《正则表达式》之后一直手痒,想自己写一个。趁着周末有空试验了一把。


首先是挑选正则表达式的元字符集,我选择了以下这些元字符:

// c 匹配任意的字母(大小写均可)
// . 任意字符
// * 重复前一字符0次或多次,但尽量多次(贪婪匹配)
// ^ 匹配串的开头
// $ 匹配串的结尾
// \ 转义字符(\c表示一个字母'c',\\表示一个字母'\')

在《代码之美》里面缺少转义字符,但是在一个完备的正则表达式中,转义字符是必不可少的(否则会有不能表示的字符),所以我在代码实现中加入了转义字符。

另外《代码之美》一书中的代码对*元字符采用的是简单的懒惰匹配,而在正规的正则表达式中,*、+、?都是采用的贪婪匹配的。从代码实现上来说贪婪匹配稍难于懒惰匹配。


另外,《代码之美》一书中也提到了如果涉及到转义字符、中括号表示的字符集时,最好用结构体的方式来表示正则表达式中的一个字符。


我的源代码中在进行正则表达式匹配之前,先对正则表达式的正确性做验证,看正则表达式是否正确。罗列的标准如下:

  1. 元字符*不能在开头,元字符^只能出现在表达式开头,元字符$只能出现在表达式末尾。
  2. 转义字符\后面只能跟元字符
  3. 元字符^后面不能是元字符*
  4. 元字符*后面不能是元字符*
一些正则表达式是否正确的测试样例:
"^^now err"     is error regular expression!
"^*abcd"        is error regular expression!
"ababcd\"       is error regular expression!
"^^*bcd"        is error regular expression!
"a^*bcd"        is error regular expression!
"^ab$cd"        is error regular expression!
"a**abcd"       is error regular expression!
"*abcd"         is error regular expression!
"\a*bcd"        is error regular expression!
"now ok"        is correct regular expression!
"\\a*bcd"       is correct regular expression!
"\**bcd"        is correct regular expression!
".*abcd"        is correct regular expression!
"^a*bcd"        is correct regular expression!
"^abcd$"        is correct regular expression!
".abc d"        is correct regular expression!
".*abcd"        is correct regular expression!
"\c*abcd"       is correct regular expression!
"\*abcd"        is correct regular expression!
"c*abcd"        is correct regular expression!
"\.c*abcd"      is correct regular expression!
"abc\.c*abcd"   is correct regular expression!
"abc  d"        is correct regular expression!
"\^*abcd"       is correct regular expression!

检测正则表达式是否正确的代码:

bool CheckRegExpr(const char *pReg)
{
    const char *pBegin = pReg;
    if ('*' == *pBegin)
        return false;

    while (*pReg)
    {
        if ( ('^' == *pReg && pReg != pBegin) || 
            ('^' == *pReg && pReg[1] == '*') || 
            ('$' == *pReg && pReg[1] != '\0') )
            return false;

        if ('*' == *pReg && '*' == pReg[1])
            return false;

        if ('\\' == *pReg)
        {
            if (!IsRegMetacharacter(pReg[1]))
                return false;
            else
                pReg++;
        }

        pReg++;
    }
    return true;
}

正则匹配的核心代码:

const char* RegExprFind(const char *pText, const char *pReg)
{
    const char *pCur = pText;
    if (!CheckRegExpr(pReg))
        return (char*)-1;

    do 
    {
        g_pBeg = pCur;
        MATCH_STATE eResult = Match(pCur, pReg);
        if (MATCH_OK == eResult)
            return pCur;
        else if (MATCH_ERROR == eResult)
            return (char*)-1;
        else if (MATCH_FAIL == eResult)
            return NULL;
    }
    while (*pCur++);
    return NULL;
}

MATCH_STATE Match(const char *pCur, const char *pReg)
{
    g_pEnd = pCur;
    if ('\0' == *pReg)
        return (g_pEnd != g_pBeg) ? MATCH_OK : MATCH_NEXT_POSITION;

    if ('$' == *pReg)
        return ('\0' == *pCur && g_pEnd != g_pBeg) ? MATCH_OK : MATCH_FAIL;

    if ('^' == *pReg)
    {
//        return Match(pCur, pReg+1);
        if (MATCH_OK == Match(pCur, pReg+1))
            return MATCH_OK;
        else
            return MATCH_FAIL;
    }

    st_RE_Element elementCur;// 更复杂的情况要借助结构体
    st_RE_Element elementNext;
    int nElementLen1 = 0;
    int nElementLen2 = 0;
    nElementLen1 = GetReElement(&elementCur, pReg);
    nElementLen2 = GetReElement(&elementNext, pReg+nElementLen1);

    if (!elementNext.isEscape && elementNext.ch == '*')// 贪婪匹配比较麻烦
    {
        const char *pStart = pCur;
        while(*pCur && MatchAt(elementCur, *pCur))
            pCur++;
        while (pCur >= pStart)
        {
            if (MATCH_OK == Match(pCur, pReg+nElementLen1+nElementLen2))
                return MATCH_OK;
            pCur--;
        }
    }
    else
    {
        if (MatchAt(elementCur, *pCur))
            return Match(pCur+1, pReg+nElementLen1);
    }

    return MATCH_NEXT_POSITION;
}

int GetReElement(pst_RE_Element pElement, const char *pReg)
{
    if (*pReg == '\\')
    {
        pElement->isEscape = true;
        pElement->ch = pReg[1];
        return 2;
    }
    else
    {
        pElement->isEscape = false;
        pElement->ch = *pReg;
        return 1;
    }
}

bool MatchAt(st_RE_Element regChar, char ch)
{
    if (regChar.isEscape) // \c \\ etc.
    {
        return ch == regChar.ch;
    }
    else // a . c
    {
        if ('.' == regChar.ch || ('c' == regChar.ch && IsAlpha(ch)) )
        {
            return true;
        }
        return ch == regChar.ch;
    }
}


正则表达式对应的结构体和枚举常量:

typedef struct _st_RE_Element
{
    char ch;
    bool isEscape;
}st_RE_Element, *pst_RE_Element;

enum MATCH_STATE
{
    MATCH_OK,
    MATCH_NEXT_POSITION,
    MATCH_FAIL,
    MATCH_ERROR, //regular expression syntax error, like "a**b"
};

正则表达式匹配效果演示:

                                        0         1         2         3         4         5
                                        012345678901234567890123456789012345678901234567890
Text is                                 Let's ggggo abcdeccc.^$*\\\fg 223333332122222333322222
Find at 00      RegExpr  =  "Let"       Let
Find at 00      RegExpr  =  "^Let.*$"   Let's ggggo abcdeccc.^$*\\\fg 223333332122222333322222
Find at 00      RegExpr  =  "c*"        Let
Find at 06      RegExpr  =  "g*o*"      ggggo
Find at 06      RegExpr  =  "g*a*"      gggg
Find at 30      RegExpr  =  "k*2*3*k*"  22333333
Find at 30      RegExpr  =  "k*2*k*3*"  22333333
Find at 22      RegExpr  =  "\$"        $
Find at 20      RegExpr  =  "\."        .
Find at 24      RegExpr  =  "\\*"       \\\
Find at 20      RegExpr  =  "\..*23"    .^$*\\\fg 2233333321222223
Find at 00      RegExpr  =  "c*"        Let
Find at 14      RegExpr  =  "\c*"       c
Find at 49      RegExpr  =  "2*$"       22222
Find at 32      RegExpr  =  "3*"        333333
Find at 30      RegExpr  =  "2*223"     223
Find at 30      RegExpr  =  "2*23"      223
Find at 06      RegExpr  =  "g*o.*3*21" ggggo abcdeccc.^$*\\\fg 2233333321
Find at 06      RegExpr  =  "g*o.*3*2"  ggggo abcdeccc.^$*\\\fg 223333332122222333322222
Find at 06      RegExpr  =  "g*o.*3*"   ggggo abcdeccc.^$*\\\fg 223333332122222333322222
Find at 06      RegExpr  =  "g*o.*3"    ggggo abcdeccc.^$*\\\fg 2233333321222223333
Not Found!      RegExpr  =  "Below are Not Found!------------------------------------------"
Not Found!      RegExpr  =  "k*"
Not Found!      RegExpr  =  "g*o.2*3"
Find at 54      RegExpr  =  "3*$"
Not Found!      RegExpr  =  "^3*"
Not Found!      RegExpr  =  "Below are Syntax Error!---------------------------------------"
Syntax error!   RegExpr  =  "^*abc"
Syntax error!   RegExpr  =  ".*a$bc"

完整代码可到以下地址下载:

http://download.csdn.net/detail/sun2043430/5333836

注:代码中使用函数式实现,为了便于打印输出、展示结果,使用了全局变量。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个使用Binding方法实现的WebView微型浏览器的示例代码。 在 XAML 文件中,我们需要添加一个 WebView 控件和一个 TextBox 控件用于输入网址: ```xaml <Grid> <WebView x:Name="webView" /> <TextBox x:Name="urlTextBox" TextChanged="UrlTextBox_TextChanged" /> </Grid> ``` 在 C# 代码中,我们需要创建一个名为 BrowserViewModel 的类,并在其中添加一个名为 NavigateCommand 的命令。这个命令将在用户输入网址并按下 Enter 键时被触发。 ```csharp using System.Windows.Input; using Windows.UI.Xaml.Controls; namespace WebViewMiniBrowser { public class BrowserViewModel { public ICommand NavigateCommand { get; } public BrowserViewModel() { NavigateCommand = new RelayCommand<string>(Navigate); } private void Navigate(string url) { webView.Navigate(new Uri(url)); } private WebView webView; public void SetWebView(WebView webView) { this.webView = webView; } } } ``` 在这个类中,我们创建了一个 RelayCommand 泛型类,它将在命令被触发时执行 Navigate 方法。我们还添加了一个 SetWebView 方法,用于在视图中设置 WebView 控件的引用。 现在,我们需要在视图的代码中创建一个 BrowserViewModel 的实例,并将其与视图绑定起来。我们还需要将视图中的 WebView 控件传递给 BrowserViewModel。 ```csharp public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); var viewModel = new BrowserViewModel(); viewModel.SetWebView(webView); DataContext = viewModel; } private void UrlTextBox_TextChanged(object sender, TextChangedEventArgs e) { var viewModel = DataContext as BrowserViewModel; viewModel.NavigateCommand.Execute(urlTextBox.Text); } } ``` 在这个代码中,我们在 MainPage 构造函数中创建了 BrowserViewModel 的实例,并将其与视图进行绑定。我们还在 SetWebView 方法中将视图中的 WebView 控件传递给 BrowserViewModel。 最后,我们在 TextChanged 事件处理程序中获取 BrowserViewModel 的实例,并执行 NavigateCommand 命令来导航到用户输入的网址。 这样,我们就完成了一个使用 Binding 方法实现的 WebView 微型浏览器。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值