怎么用C#实现正则表达式的匹配(剑指OFFER面试题第十九题)

经过两天的课设+考试,终于有时间继续看书了,今天记录一道面试题“正则表达式的匹配”。
问题描述:请实现一个函数用来匹配包含’.’和’‘的正则表达式。模式中的字符’.’表示任意一个字符,而’‘表示它前面的字符可以出现任意次(包括0次)。在本题中,匹配是指字符串的所有自负匹配整个模式。

例如:字符串”aaa”与模式”a.a”和”ab*ac*a”匹配,但与”aa.a”和”ab*a”均不匹配。

一开始没看懂题,后来读了好几遍发现,就是一个简单的检测字符串,意思是,给你一个模式和一个字符串,然后,看看是否匹配,字符’.’可以用任意字符代替。

比如”a.a”那么和”a&a”、”aka”、”a/a”都匹配,只要两边是a,那么中间那个字符’.’可以随意变换。同理,字符‘’前面的那个字符可以出现无数次,也可以出现0次,比如”ab*a”,那么它与”aa”(b出现0次)、”aba”(b出现1次)、”abbbbbbbbbba”(b出现10次)、”ab······ba”(b出现n次)都能匹配,因为其他字符不变,字符’‘前面的字符出现多少次都不影响字符串的匹配。那么题目意思理解了,后面就容易了。

思路:
每次从字符串中拿出一个字符和模式中的字符去匹配。先来分析如何匹配一个字符,如果模式中的字符ch是’.’,那么它可以匹配字符串中的任意字符。如果模式中的字符ch是’.’,而且字符串中的字符也是ch,那么他们相互匹配。

当模式中的第二个字符不是’*’时,如果字符串的第一个字符和模式的第一个字符相匹配,那么字符串和模式都向后移一个字符,然后匹配剩余的字符串和模式。如果字符串中的第一个字符和模式的第一个字符不匹配,则直接返回false。

当模式中的第二个字符是‘*’的时候,因为可能有多种不同的匹配方式:
方式一:在模式上向后移动两个字符,相当于前面的字符和‘’被忽略了(模式‘’前面的字符出现了0次)
方式二:当前模式的字符与字符串的字符相匹配,同时向后移一位(模式‘*’前面的字符只出现一次)
方式三:字符串向后移一位,模式上不变(模式上‘*’前面的字符出现多次)

以上,就是对于正则表达式匹配的分析,有点绕脑,看代码你会觉得清晰很多。
当然,我还进行了一系列可能的测试,在我能考虑到的范围内,都进行了测试,全部PASS,以下为通过代码与测试。

using System;
namespace 正则表达式
{
    class Program
    {
        static void Main(string[] args)
        {
            string pattern1 = "ab*a.a.*a";
            //测试一:两个字符'*'的前面的字符个数均为0
            string str1 = "aaaaa";
            char[] ch = str1.ToCharArray();
            char[] pattern = pattern1.ToCharArray();
            if (Match(ch, pattern))
                Console.WriteLine("Test1 Pass");
            else
                Console.WriteLine("Test1 Error");
            //测试二:字符'*'之前的字符只出现一次
            string str2 = "abaaaa";
            ch = str2.ToCharArray();
            if (Match(ch, pattern))
                Console.WriteLine("Test2 Pass");
            else
                Console.WriteLine("Test2 Error");
            //测试三:模式字符'*'的前一个字符出现n次
            string str3 = "abbbbbbaaaa";
            ch = str3.ToCharArray();
            if (Match(ch, pattern))
                Console.WriteLine("Test3 Pass");
            else
                Console.WriteLine("Test3 Error");
            //测试四:模式字符'*'的前一个字符为'.',匹配任意字符n次
            string str4 = "abaaa222*a";
            ch = str4.ToCharArray();
            if (Match(ch, pattern))
                Console.WriteLine("Test4 Pass");
            else
                Console.WriteLine("Test4 Error");
            //测试五:匹配的字符串为空
            ch = null;
            if (Match(ch, pattern) == false)
                Console.WriteLine("Test5 Pass");
            else
                Console.WriteLine("Test5 Error");
            //测试六:模式字符串为空
            ch = str1.ToCharArray();
            pattern = null;
            if (Match(ch, pattern) == false)
                Console.WriteLine("Test6 Pass");
            else
                Console.WriteLine("Test6 Error");
            //测试七:正则表达式为".*"且字符串中都没有字符
            string str7 = "";
            string pattern7 = ".*";
            ch = str7.ToCharArray();
            pattern = pattern7.ToCharArray();
            if (Match(ch, pattern))
                Console.WriteLine("Test6 Pass");
            else
                Console.WriteLine("Test6 Error");
        }

       public static bool Match(char[] str, char[] pattern)
       {
           //如果传入的字符数组是空,则匹配失败
           if (str == null || pattern == null)
               return false;

           return MatchCore(str, pattern, 0, 0);
       }

        /// 
        /// 用来匹配字符串和正则表达式
        /// 
        /// 匹配的字符串
        /// 匹配的正则表达式
        /// 字符串的匹配位置
        /// 正则表达式的匹配位置
        /// 
        private static bool MatchCore(char[] str, char[] pattern, int strIndex, int patternIndex)
        {
            //如果字符串和模式的匹配都到了最后一位,那么返回真
            if (strIndex == str.Length && patternIndex == pattern.Length)
                return true;
            //字符串未匹配完但是正则表达式已经匹配完毕,返回假
            else if (strIndex < str.Length && patternIndex == pattern.Length)
                return false;

            //正则表达式匹配的下一位是'*'
            if (patternIndex + 1 <= pattern.Length - 1 && pattern[patternIndex + 1] == '*')
            {
                //如果当前字符串和模式的字符匹配,或者模式的字符为'.'且字符串还有字符没有匹配,这里需要注意的是,一定得在这里判断strIndex是否小于str.Length,因为在测试中发现,str为"",pattern为".*"的时候,如果没有这个判断,就会执行里面的if语句,虽然会返回true,但是会数组越界,下面的if同理
                if (strIndex < str.Length && (pattern[patternIndex] == str[strIndex] || pattern[patternIndex] == '.'))
                {
                    //'*'前面的字符出现了0次
                    return MatchCore(str, pattern, strIndex, patternIndex + 2) ||
                            //'*'前面的字符出现了N次
                            MatchCore(str, pattern, strIndex + 1, patternIndex) ||
                            //'*'前面的字符只出现了一次
                            MatchCore(str, pattern, strIndex + 1, patternIndex + 2);
                }
                else
                {
                    //当前正则表达式匹配的字符不匹配,那么字符向后移两位
                    return MatchCore(str, pattern, strIndex, patternIndex + 2);
                }
            }
            //如果当前模式与字符串的字符匹配,或当前模式上的字符为'.'且字符串尚未匹配完,同时向后移一位
            if (strIndex < str.Length && (str[strIndex] == pattern[patternIndex] || pattern[patternIndex] == '.'))
                return MatchCore(str, pattern, strIndex + 1, patternIndex + 1);

            return false;
        }
    }
}

你可能感兴趣的:(编程学习,C#)