文件名通配符匹配算法

什么是文件名通配符


如何实现带有?*通配符的字符串(string)匹配呢,可以把问题拆开看
只含有?的模型(pattern),和只含有*的模型

只含有?的模型理解起来非常简单,因为?就等于一个字符,含有?的模型就等于一个字符串
判断是否匹配,只要在给定字符串中,查找含有?的字符串就行了,而在字符串中查找字符串就是用indexOf.
只不过现有的indexOf,不知道'?'就代表任意字符,所以需要自己动手改写一下.
程序如下
function indexOf(str: String, find: String, pos: Number): Number
{
    if(undefined == str || undefined == find ) return;
    if(undefined == pos || pos < 0) pos = 0;

    var si=pos, slen=str.length, fi=0, flen=find.length;
    while(si<slen && fi<flen)
    {
        if( str.charAt(si) == find.charAt(fi)
            || find.charAt(fi) == '?' ) // 碰到?号,可以继续往下比较
        {
            si++; fi++;
        }
        else
        {
            si = si-fi+1;
            fi=0;
        }
    }
    if( fi==flen ) return si-flen;
    else return -1;
}
//-Example
var string = "abaabbabc";
var pattern = "a?babc";
trace( indexOf(string, pattern) ); // 3

可以看到,经过我们改写的indexOf,已经把'?'当作任意字符了.
而lastIndexOf的改写也是类似,不同的地方要注意字符的比较方向和indexOf相反

现在使用改写了的indexOf或者lastIndexOf就可以解决含有?的模型匹配问题了.
因为只需要在查到的同时再比较一下string和pattern的长度,看看是否相同就可以得出判断.
为了提高效率,可以把长度判断放在查询前面.

function matchWithInterrogation(str: String, pattern: String): Boolean
{
    if(str.length != pattern.length ) return false;
    else return indexOf(str, pattern) > -1;
}

------------------------------------------------------------------------

接着,我们来看只含有*通配符的模型匹配算法.
因为一个*代表的可以是0个或者N个的字符,所以一个含有*通配符的模型,是不能确定长度的.
不能简单的认为是pattern.length,这个长度值,只是把*看作普通字符来统计.
另外,几个连续的*的意义和一个*的意义是相同的,比如
*代表0-n个字符
**也代表0-n个字符....这个不用多解释了吧~

由于*的特殊性,我们无法确定它所代表的长度,反过来说,我们又可以认为它是任意长度.
也就说,可以把*看作是空字符,1,a,123,abcd!@$#%^%等等这样的字符或字符串.

既然这样,我们就不能把string和pattern按照逐字的形式一个一个来比较.
但可以变成一串一串来比较,而且可以不用管串与串之间是否是紧挨着的.
什么意思呢?
比如
string: Oh year.Totay is weekend!
pattern: *ye*a*e*

模型的有效字符是y,e,a,e
ye是连在一起的,可以把ye当成一个串来看,其它的也可以看作是一个串,只是这样的串长度为1.

我们先在string中找ye,找到了唯一存在的地方Oh (ye)ar....
接着找a,发现ye后面就有一个a,那么Oh (yea)r....
然后找e,Oh (yea)r.Totay is w(e)ekend!
于是找完了,但是我们发现a不一定在ye后面,在Totay中也有一个a,而e的话weekend里面就有3个,那是不是一定要按照上面的方法找呢?

是的.这里有个就近原则,如果从左向右找,就以在未查询过部分的最左边找到为匹配,从右向左找,亦然.
因为最近的被匹配掉了,还有远的可以用来再匹配.
假如不是这样,那就有可能出现应该匹配的而匹配不到
比如
weekend
*e*e*e*
如果此时不用就近原则,就无法完成3个e的匹配(前两个e间的*,可以认为是空字符)

使用就近原则,还利于方便程序的编写和方法的理解.

通过上面的解释,发现含有*通配符的模型,只是在string中不停的依次搜索而已.既然是搜索当然要用到indexOf这个函数啦,哈哈是不是很眼熟.
那怎么用呢?很自然想到,把含有*的模型,以*为分界线,分成几个包含有效字符的串.
然后以就近原则在指定的string里面,逐个的把这些串找出来,如果都找出来了,那就是匹配的,只要一个串没有找到,那就是不匹配了.
因为前面我们已经改写了indexOf这个函数,使得它可以辨别?通配符,所以在这里,就可以把?当成是一个串中的有效字符来理解.

我们还要感谢flashplayer提供了String.split这个函数,哈哈,分割的任务就靠它了.现在我们万事俱备就欠编程.

在写程序之前,有几个注意事项.
第一,注意模型的头是*,还是有效字符
如果是*,那*后的第一个串,可以在string的任意位置
如果是有效字符,那第一个串,一定也要在string的头
尾部也是如此
第二,注意几个极端现象.
a)模型没有*,那就是一个含?字符串的匹配,可以用前面的matchWithInterrogation函数来解决
b)模型只有*,前面说了连续的*意义等同于只有一个*,而只有一个*就说明string可以是任意字符,包括空
c)模型为空,也就是只有string为空时才匹配
d)由bc认为,连续的*间存在一个空字符,空字符匹配空字符,而在字符串范围内查找空字符,就返回搜索的起始位置.
第三,注意String.split的分割符在字符串的开头或者末尾时,第一或最后一个元素为空字符

好了,可以开始动手写程序了
function matchWithAsterisk(str: String, pattern: String): Boolean
{
    if(undefined == str || undefined == pattern ) return;

    var ps = pattern.split('*');
    if( 1==ps.length ) // 没有*的模型
        return matchWithInterrogation(str, ps[0]);
   
    var si = indexOf(str, ps[0], 0); // 从string头查找第一个串
    if( 0 != si ) return false; // 第一个串没找到或者不在string的头部
    si += ps[0].length; // 找到了串后,按就近原则,移到未查询过的最左边

    var plast=ps.length-1; // 最后一串应单独处理,为了提高效率,将它从循环中取出
    var pi=0; // 跳过之前处理过的第一串
    while(++pi<plast)
    {
        if(''==ps[pi]) continue; //连续的*号,可以忽略
        si = indexOf(str, ps[pi], si); // 继续下一串的查找
        if(-1==si) return false; // 没有找到
        si += ps[pi].length; // 就近原则
    }

    if(''==ps[plast]) // 模型尾部为*,说明所有有效字符串部分已全部匹配,string后面可以是任意字符
        return true;

    // 从尾部查询最后一串是否存在
    var last_index = lastIndexOf(str, ps[plast]);
    // 如果串存在,一定要在string的尾部, 并且不能越过已查询过部分
    return (last_index == str.length - ps[last].length) && (last_index >= si);
}
//-Example
var string = "Oh year.Totay is weekend!";
var pattern = "*ye*a*e*";
trace( match(string, pattern) ); // true
var string = "weekend";
var pattern = "e*e*e";
trace( match(string, pattern) ); // false

至此,文件名通配符匹配算法,已经解析完了.
遗留了lastIndexOf的改写代码,不过这个和indexOf没什么太大区别,所以就懒得写了=_=
另外,matchWithAsterisk是以从左到右来查询的,有兴趣的可以试试从右到左,感觉可能会不太一样^^_
还有,由于AS写的代码运行效率不高-特别是AS1/2,真到了3,都支持正则了,谁还来看这破玩意orz-所以像改写的
indexOf/lastIndexOf方法,可以采用类似*号匹配算法那样,将'?'分割出来,然后按串来比较,这样可以使用一些player
内置的方法,要比单纯的用AS逐个判断效率高.


我写了一个 Wildcard类,就是用内置的一些函数组合的来实现indexOf/lastIndexOf,当然整个过程要复杂的多,而且在索引的
加1减1上很容易产生混淆,有兴趣的朋友可以 下载下来看看.

效率提升的幅度和分割符间的有效字符串长度成正比,在最坏情况下,改进算法效率反而略低于上述算法-_-||不过提升的幅度是明显的.


题外话:
我也是一时兴起,决定写个通配符玩玩,在数据结构和算法不怎么样的情况下,写的途中发现很多问题,虽都一一解决,但并不完美,也可能还存在一些问题
在此写下学习经验,希望各方拍.
这里是c++语言版本,仅供娱乐~_~
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值