commons-io之WildcardFileFilter的实现

上次这个是最后一个FileFilter,没想到这个实现的还比较复杂,当时头脑比较晕,也没有看懂,就想这次专门的看一下源码,在看这个Filter之前,我们必须要理解FilenameUtils里面的wildMatch函数的实现,这个是最经典的通配符的匹配,可以好好看看。

 

       1. 首先我们应该来看一个小函数的实现splitOnTokens

   这个函数实际上还是比较简单的,它的主要意图就是要将字符串按*和?进行分割,这个实际上也比较简单,下面我们就来看一些它的具体实现。


   首先判断如果字符串里面不包含*或者?,则直接将字符串包装成String[]进行返回,这个比较好理解然后将字符串转char类型的array,进行遍历,如果发现字符是*或者?,则判断StringBuffer里面的字符长度是不是空,并将字符串加入的List里面,并把当前的buffer清空,如果是?则直接也将?加入到List里面,如果是*的情况就稍微复杂一些了。首先如果list是空,则直接将*加入到list里面,这种情景我们可以想象成第一个我们遇到了*,如果list不为空,我们就判断list的最后一个元素是不是*,如果这两个有一个满足,就把*加入到list里面,第二种情况我们也很好理解,主要是在通配符里面,如果是*的话是代表任意字符的,所以是不需要把两个连一块的*加入到list里面的。最后将list转化成String[]进行返回。


   这个函数里面有一个判断,i>0,真不知道这个是干什么的,我感觉只要list不为空就可以取元素了,这个可能还有等高人进行解释。

Java代码   收藏代码
  1. static String[] splitOnTokens(String text) {  
  2.     // used by wildcardMatch  
  3.     // package level so a unit test may run on this  
  4.   
  5.     if (text.indexOf("?") == -1 && text.indexOf("*") == -1) {  
  6.         return new String[] { text };  
  7.     }  
  8.   
  9.     char[] array = text.toCharArray();  
  10.     ArrayList list = new ArrayList();  
  11.     StringBuffer buffer = new StringBuffer();  
  12.     for (int i = 0; i < array.length; i++) {  
  13.         if (array[i] == '?' || array[i] == '*') {  
  14.             if (buffer.length() != 0) {  
  15.                 list.add(buffer.toString());  
  16.                 buffer.setLength(0);  
  17.             }  
  18.             if (array[i] == '?') {  
  19.                 list.add("?");  
  20.             } else if (list.size() == 0 ||  
  21.                     (i > 0 && list.get(list.size() - 1).equals("*") == false)) {  
  22.                 list.add("*");  
  23.             }  
  24.         } else {  
  25.             buffer.append(array[i]);  
  26.         }  
  27.     }  
  28.     if (buffer.length() != 0) {  
  29.         list.add(buffer.toString());  
  30.     }  
  31.   
  32.     return (String[]) list.toArray( new String[ list.size() ] );  
  33. }  
 

 

   2. wildcardMatch函数的理解

   这个函数特别不好理解,里面的变量命名wcs, textIdx,wcsIdx这样的很多,使得我们不能很好的把握作者的意图,这里我们可以看到一个好的命名是多么的重要。

 

 

Java代码   收藏代码
  1. public static boolean wildcardMatch(String filename, String wildcardMatcher, IOCase caseSensitivity) {  
  2.         // 在fileName和wildcardMatcher都为空的情况下,直接返回true,这样也比较符合情理  
  3.         if (filename == null && wildcardMatcher == null) {  
  4.             return true;  
  5.         }  
  6.         // 在filename和wildcardMatcher有一个为空的情况下,返回false,这个也比较好理解  
  7.         if (filename == null || wildcardMatcher == null) {  
  8.             return false;  
  9.         }  
  10.         // 如果caseSensitivity为空的情况下,我们默认为是sensitive  
  11.         if (caseSensitivity == null) {  
  12.             caseSensitivity = IOCase.SENSITIVE;  
  13.         }  
  14.         // 将filename转化为相应的形式,主要是case sensitive的时候全部转小写  
  15.         filename = caseSensitivity.convertCase(filename);  
  16.         // 将wildcase转化为相应的形式,主要是case sesnitive的时候全部转化成小写  
  17.         wildcardMatcher = caseSensitivity.convertCase(wildcardMatcher);  
  18.         // 这个函数我们再上面已经讲过了,主要是讲通配符进行分割,然后转化成String[]进行返回  
  19.         String[] wcs = splitOnTokens(wildcardMatcher);  
  20.         /* 
  21.          * 下面的这几个变量的意图我把握的不是很好,请大家在分析后进行补充,anyChars感觉是判断是不是 
  22.          * 匹配任意字符,textIdx用来指示现在匹配到那个字符了,wscIdx主要是来标明现在在使用那个通配 
  23.          * 符,也就是wcs的下标, backtrack是一个栈,用来处理复杂的*匹配,如果里面出现多个匹配上的, 
  24.          * 可能还需要进行回溯处理,这个地方也是这个函数最难理解的地方。 
  25.          */  
  26.         boolean anyChars = false;  
  27.         int textIdx = 0;  
  28.         int wcsIdx = 0;  
  29.         Stack backtrack = new Stack();  
  30.   
  31.         // loop around a backtrack stack, to handle complex * matching  
  32.         do {  
  33.             /* 
  34.              * 如果backtrack的长度大于0,说明有多个项已经匹配上了,所以要从栈里面进行恢复wcsIndex和 
  35.              * textIdx重新进行匹配 
  36.              */  
  37.             if (backtrack.size() > 0) {  
  38.                 int[] array = (int[]) backtrack.pop();  
  39.                 wcsIdx = array[0];  
  40.                 textIdx = array[1];  
  41.                 anyChars = true;  
  42.             }  
  43.   
  44.             // loop whilst tokens and text left to process  
  45.             while (wcsIdx < wcs.length) {  
  46.   
  47.                 // 如果是?,那么是匹配一个字符,所以当前的textIdx增加一个,anyChars当然是false了,这个很好理解  
  48.                 if (wcs[wcsIdx].equals("?")) {  
  49.                     // ? so move to next text char  
  50.                     textIdx++;  
  51.                     anyChars = false;  
  52.                 // 如果是*,这个anyChars当然是true了,还有就是如果是最后一个串的话,textIdx为字符串  
  53.                 // 的长度,说明已经处理完了  
  54.                 } else if (wcs[wcsIdx].equals("*")) {  
  55.                     // set any chars status  
  56.                     anyChars = true;  
  57.                     if (wcsIdx == wcs.length - 1) {  
  58.                         textIdx = filename.length();  
  59.                     }  
  60.   
  61.                 } else {  
  62.                     // matching text token  
  63.                     if (anyChars) {  
  64.                         // 这个分支主要是来处理*的通配符的,首先在匹配串里面查找,并把下标给标示  
  65.                         // 处理到那个字符的下标,如果没有找到,则表明没有匹配上,并进行返回  
  66.                         // any chars then try to locate text token  
  67.                         textIdx = filename.indexOf(wcs[wcsIdx], textIdx);  
  68.                         if (textIdx == -1) {  
  69.                             // token not found  
  70.                             break;  
  71.                         }  
  72.                           
  73.                         // 再尝试查找字符串里面还有没有相应的字串出现了,如果有的话,将当前的匹配串和出现的下标  
  74.                         // 进行压栈,等待下一次的回溯处理。  
  75.                         int repeat = filename.indexOf(wcs[wcsIdx], textIdx + 1);  
  76.                         if (repeat >= 0) {  
  77.                             backtrack.push(new int[] {wcsIdx, repeat});  
  78.                         }  
  79.                     } else {  
  80.                         // 如果不是匹配不上的话,直接break,这个也比较好理解  
  81.                         // matching from current position  
  82.                         if (!filename.startsWith(wcs[wcsIdx], textIdx)) {  
  83.                             // couldnt match token  
  84.                             break;  
  85.                         }  
  86.                     }  
  87.   
  88.                     // matched text token, move text index to end of matched token  
  89.                     textIdx += wcs[wcsIdx].length();  
  90.                     anyChars = false;  
  91.                 }  
  92.   
  93.                 wcsIdx++;  
  94.             }  
  95.   
  96.             // full match  
  97.             if (wcsIdx == wcs.length && textIdx == filename.length()) {  
  98.                 return true;  
  99.             }  
  100.   
  101.         } while (backtrack.size() > 0);  
  102.   
  103.         return false;  
  104.     }  

    上面的我知道我可能理解的很不好,只能说是马马虎虎的看懂了代码,但对作者的意图等的完全不明白,看来自己的功力还真的很有限。 这里需要说明的是,我在理解这个代码的时候调试了很多次,也看了一下它的单元测试。其中我感觉两个稍微有意义的我摘取出来了,希望对大家理解代码有所帮助。

    FilenameUtils.wildcardMatch("New Bookmarks", "N?w ?o?k??r?s")

Java代码   收藏代码
  1. FilenameUtils.wildcardMatch("Foo Bar Foo""F*o Bar*")  
  2. FilenameUtils.wildcardMatch("log.log""*log")  
 

  还有,这个类名叫: FilenameUtils, 方面名叫: wildcardMatch

  希望大家有时间了也分析一下,好好把这个再弄明白一些,也欢迎大家交流。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值