zoukankan      html  css  js  c++  java
  • Struts2使用通配符加载配置文件和通配符方法的底层实现学习

    Struts2中,可以在struts2的配置文件中,使用include的节点,来加载配置文件。例如:

     1 <!DOCTYPE struts PUBLIC
     2   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
     3   "http://struts.apache.org/dtds/struts-2.0.dtd">
     4 <struts>
     5     <include file="Home.xml"/>
     6     <include file="Hello.xml"/>
     7     <include file="Simple.xml"/>
     8     <include file="/util/POJO.xml"/>
     9     <include file="/com/initech/admin/admin-struts.xml"/>
    10 </struts>

    在其中可以使用struts2提供的通配符来加载文件(在struts2的官方帮助文档中没有找到include支持通配符的说明,但是

    在代码中确实发现,它支持通配符的配置):

    1 <struts>
    2     <include file="com/struts/config/struts_*.xml"></include>
    3 </struts>

    在通配符的配置中:**代表匹配path,即录用

                             *代表匹配文件名称,即filename,

                              \代表转义符,即\*代表*

    下面是摘自官网对通配符的介绍:如下:

    Wildcard Mappings

    Wildcard patterns can contain one or more of the following special tokens:

    *   :   Matches zero or more characters excluding the slash ('/') character.

    ** :   Matches zero or more characters including the slash ('/') character.

    \   :  character The backslash character is used as an escape sequence. Thus  '\*'

    matches the character asterisk ('*'), and'\\' matches the character backslash ('\').

     注意:下面是官网给出的一句非常重要的话:

          Mappings are matched against the request in the order they appear in the framework's configuration file. If more than one pattern matches the last one wins, so less specific patterns must appear before more specific ones. However, if the request URL can be matched against a path without any wildcards in it, no wildcard matching is performed and order is not important. Also, note that wildcards are not greedy, meaning they only match until the first occurrence of the following string pattern. For example, consider the following mapping:

         在和request请求中,匹配的顺序和它们在框架的配置文件的加载次序是相同的,如果有多个配置文件和请求匹配 成功,那么最后一个将会被执行,所以,少的特殊的配置必须在之前指定(假如即配置了通配符,又配置了普通的 struts2的配置,即通过request请求,通配符,也可以匹配成功,但是普通配置也可以匹配到,例如,下面的 例子),但是,如果一个request请求,不通过通配符也可以匹配到,那么通配符将不起作用,它不会被执行,在配置文件中的顺序也不重要了(即使在通配符之前配置了映射关系),例如下面的配置:

    1 <action name="List*s" class="actions.List{1}s">
    2      <result>list{1}s.jsp</result>
    3 </action>

         This mapping would work correctly for the URI ListAccounts but not ListSponsors, because the latter would turn into this configuration:

    1 <action name="ListSpons" class="actions.ListSpons">
    2   <result>listSpons.jsp</result>
    3 </action>

        通配符,对ListAccounts起作用,但是对ListSponsors不起作用,因为ListSponsors有单独的配置,它以单独的配置为主,不会在使用通配符。记住这个非常重要的描述,在使用通配符的前提是我们遵守约定大于配置

    在Struts2中,XmlConfigurationProvider类中实现了include配置文件使用通配符的功能,具体的代码如下:在loadConfigurationFiles方法中:

     1 for (Document doc : docs) {
     2                 Element rootElement = doc.getDocumentElement();
     3                 NodeList children = rootElement.getChildNodes();
     4                 int childSize = children.getLength();
     5 
     6                 for (int i = 0; i < childSize; i++) {
     7                     Node childNode = children.item(i);
     8 
     9                     if (childNode instanceof Element) {
    10                         Element child = (Element) childNode;
    11 
    12                         final String nodeName = child.getNodeName();
    13 
    14                         if ("include".equals(nodeName)) {--include节点
    15                             String includeFileName = child.getAttribute("file");
    16                             if (includeFileName.indexOf('*') != -1) {--存在*证明使用了通配符
    17                                 // handleWildCardIncludes(includeFileName, docs, child);
    18                                 ClassPathFinder wildcardFinder = new ClassPathFinder();
    19                                 wildcardFinder.setPattern(includeFileName);--通配符查找
    20                                 Vector<String> wildcardMatches = wildcardFinder.findMatches();
    21                                 for (String match : wildcardMatches) {
    22                                     finalDocs.addAll(loadConfigurationFiles(match, child));
    23                                 }
    24                             } else {
    25                                 finalDocs.addAll(loadConfigurationFiles(includeFileName, child));
    26                             }
    27                         }
    28                     }
    29                 }
    30                 finalDocs.add(doc);
    31                 loadedFileUrls.add(url.toString());
    32             }

    下面我们分析下struts2提供的通配符的算法!

     1     /**
     2      * Builds a {@link java.util.Vector} containing Strings which each name a file
     3      * who's name matches the pattern set by setPattern(String). The classpath is 
     4      * searched recursively, so use with caution.--这个方法是递归调用的,使用的时候,要小心!
     5      *
     6      * @return Vector<String> containing matching filenames
     7      */
     8     public Vector<String> findMatches() {
     9         Vector<String> matches = new Vector<String>();
    10         URLClassLoader cl = getURLClassLoader();
    11         if (cl == null ) {
    12             throw new XWorkException("unable to attain an URLClassLoader") ;
    13         }
    14         URL[] parentUrls = cl.getURLs();加载本地所有的文件夹,包括jar包,在文章的下面我们对其进行了介绍
    15         compiledPattern = (int[]) patternMatcher.compilePattern(pattern);--算法的开始
    16         for (URL url : parentUrls) {
    17             if (!"file".equals(url.getProtocol())) {
    18                 continue ;如果通信协议不等于文件夹,则继续新循环
    19             }
    20             URI entryURI ;
    21             try {
    22                 entryURI = url.toURI();一定一个toURL,如果toString()得不到想要的结果
    23             } catch (URISyntaxException e) {
    24                 continue;
    25             }
    26             File entry = new File(entryURI) ;建立文件
    27             Vector<String> results = checkEntries(entry.list(), entry, "");进行扫描该文件夹下面的文件
    28             if (results != null ) {这里用到递归调用,我们在下面分析checkEntries方法的调用
    29                 matches.addAll(results);满足条件的加入到list中
    30             }
    31         }
    32         return matches;
    33     }
    34     

    我们来分析下核心的算法patternMatcher.compilePattern(pattern);

    这个算法是将字符串的翻译成一个整数的数组,并且,在开头和结尾追加了一个特殊的整数;

     1  /**
     2      * <p> Translate the given <code>String</code> into a <code>int []</code>
     3      * representing the pattern matchable by this class. <br> This function
     4      * translates a <code>String</code> into an int array converting the
     5      * special '*' and '\' characters. <br> Here is how the conversion
     6      * algorithm works:</p>将字符串转换为一个数组
     7      *
     8      * <ul>
     9      *
    10      * <li>The '*' character is converted to MATCH_FILE, meaning that zero or
    11      * more characters (excluding the path separator '/') are to be
    12      * matched.</li>:*代表匹配文件,不包含反斜杠/
    13      *
    14      * <li>The '**' sequence is converted to MATCH_PATH, meaning that zero or
    15      * more characters (including the path separator '/') are to be
    16      * matched.</li>:**代表匹配路径,包含反斜杠/
    17      *
    18      * <li>The '\' character is used as an escape sequence ('\*' is translated
    19      * in '*', not in MATCH_FILE). If an exact '\' character is to be matched
    20      * the source string must contain a '\\'. sequence.</li>
    21      *          :反斜杠\代表注意字符,\*转义为*而不是路径
    22      * </ul>
    23      *
    24      * <p>When more than two '*' characters, not separated by another
    25      * character, are found their value is considered as '**' (MATCH_PATH).
    26      * <br> The array is always terminated by a special value (MATCH_END).--以MATCH_END结束
    27      * <br> All MATCH* values are less than zero, while normal characters are
    28      * equal or greater.</p>
    29      *当出现多于两个的*的时候,并且没有任何分隔符隔开,那么将默认它为**,匹配
    30      * @param data The string to translate.
    31      * @return The encoded string as an int array, terminated by the MATCH_END
    32      *         value (don't consider the array length).
    33      * @throws NullPointerException If data is null.
    34      */
    35     public int[] compilePattern(String data) {
    36         // Prepare the arrays:准备数组
    37         int[] expr = new int[data.length() + 2];
    38         char[] buff = data.toCharArray();
    39 
    40         // Prepare variables for the translation loop:准备进行循环转换的变量
    41         int y = 0;
    42         boolean slash = false;//反斜杠
    43 
    44         // Must start from beginning//必须有开始的标志:即字符串转换完成后,多了一个开始和结束的标志位
    45         expr[y++] = MATCH_BEGIN;
    46         --首先判断第一个字符
    47         if (buff.length > 0) {
    48             if (buff[0] == '\\') {
    49                 slash = true;
    50             } else if (buff[0] == '*') {
    51                 expr[y++] = MATCH_FILE;
    52             } else {
    53                 expr[y++] = buff[0];
    54             }
    55 
    56             // Main translation loop:主要的转换循环
    57             for (int x = 1; x < buff.length; x++) {
    58                 // If the previous char was '\' simply copy this char.如果之前的字符是斜杠,代表转义
    59                 if (slash) {
    60                     expr[y++] = buff[x];
    61                     slash = false;
    62                     //如果之前的不是斜杠,那么进行两个判断:文件和路径的验证
    63                     // If the previous char was not '\' we have to do a bunch of
    64                     // checks
    65                 } else {
    66                     // If this char is '\' declare that and continue:如果是斜杠,不进行任何的操作,继                                                                         续循环
    67                     if (buff[x] == '\\') {
    68                         slash = true;
    69                         当为*的时候,检查前一个字符,主要是为了验证文件和路径
    70                         // If this char is '*' check the previous one
    71                     } else if (buff[x] == '*') {
    72                         // If the previous character als was '*' match a path:如果前一个字符已经是*,                          那么匹配路径,否则匹配文件
    73                         if (expr[y - 1] <= MATCH_FILE) {
    74                             expr[y - 1] = MATCH_PATH;:注意下标是Y-1
    75                         } else {
    76                             expr[y++] = MATCH_FILE;:注意下标是y++
    77                         }
    78                     } else {
    79                         expr[y++] = buff[x];
    80                     }
    81                 }
    82             }
    83         }
    84 
    85         // Must match end at the end
    86         expr[y] = MATCH_THEEND;
    87 
    88         return expr;
    89     }

     测试:

            PatternMatcher wildcardHelper = new WildcardHelper();
            int[] res = (int[])wildcardHelper.compilePattern("**abc");
            for (int i = 0; i < res.length; i++) {
                System.out.print(res[i]);
            }
            System.out.println();
            res = (int[])wildcardHelper.compilePattern("***abc");
            for (int i = 0; i < res.length; i++) {
                System.out.print(res[i]);
            }

    结果:

    1 -4-2979899-50 :-4代表开头,-5代表计算,后面出现的空0是由于在int[] expr = new int[data.length() + 2];
    2 -4-2979899-500:

           下面的***abc和第一个解析的结果是一致的,主要是因为上面的那句解释( All MATCH* values
    are less than zero, while normal characters are * equal or greater.):当出现多于两个的*的时候,并且没有任何分隔符隔开,那么将默认它为**,匹配,

    我们现在介绍一个在读取文件的时,经常用到的一个很有用的类的用法:

     1     public static void main(String[] args) throws URISyntaxException {
     2         URLClassLoader c = (URLClassLoader)ClassPathFinder.class.getClassLoader();
     3 //        URLClassLoader loader = (URLClassLoader)Thread.currentThread().getContextClassLoader();
     4         URL[] urls = c.getURLs();
     5         for (URL url : urls) {
     6             File entry = new File(url.toURI()) ;
     7             System.out.println(entry);
     8             if(entry.isDirectory()){
     9                 System.out.println(1);
    10             }
    11             if(entry.isFile()){
    12                 System.out.println(2);
    13             }
    14             System.out.println(entry.list());
    15         }
    16     }

    这个类的用法是将项目下的所有的目录扫描出来;包括JAR包的地址,但是要非常注意的一个问题是, File entry = new File(url.toURI()) ;而不是 File entry = new File(url.toString()) ;否则得不到想要的结果,关于这个可与参考我们的ClassPathFinder类;

     下面来分析下ClassPathFinder类的checkEntries方法;该方法使用了递归调用

     1 private Vector<String> checkEntries(String[] entries, File parent, String prefix) {
     2         entries是file.list的来的,如果没有文件,证明当前文件夹下面没有文件,直接返回
     3         if (entries == null ) {
     4             return null;
     5         }
     6         entries是file.list方法得来,该方法将文件夹下面的所以的文件和文件夹的名字放到list中
     7         Vector<String> matches = new Vector<String>();遍历该文件夹下的文件
     8         for (String listEntry : entries) {
     9             File tempFile ;
    10             if (!"".equals(prefix) ) {
    11                 tempFile = new File(parent, prefix + "/" + listEntry);
    12             }
    13             else {
    14                 tempFile = new File(parent, listEntry);
    15             }
    16             if (tempFile.isDirectory() && 如果是文件夹,则继续递归查找
    17                     !(".".equals(listEntry) || "..".equals(listEntry)) ) {
    18                 if    (!"".equals(prefix) ) {
    19                     matches.addAll(checkEntries(tempFile.list(), parent, prefix + "/" + listEntry));
    20                 }
    21                 else {
    22                     matches.addAll(checkEntries(tempFile.list(), parent, listEntry));
    23                 }
    24             }文件
    25             else {
    26                 
    27                 String entryToCheck ;
    28                 if ("".equals(prefix)) {
    29                     entryToCheck = listEntry ;
    30                 }
    31                 else {
    32                     entryToCheck = prefix + "/" + listEntry ;
    33                 }
    34                 compared是好像是做了一个缓存,但是这里搞不懂这个作用,不知道如何进行缓存,因为enteryToChe                      ck不一样都,即使文件名字相同,但是它们的文件夹不同
    35                 if (compared.contains(entryToCheck) ) {
    36                     continue;
    37                 }
    38                 else {
    39                     compared.add(entryToCheck) ;
    40                 }
    41                 这里出现了最关键的一个算法,match算法
    42                 boolean doesMatch = patternMatcher.match(new HashMap<String,String>(), entryToCheck, compiledPattern);
    43                 if (doesMatch) {
    44                     matches.add(entryToCheck);
    45                 }
    46             }
    47         }
    48         return matches ;
    49     }

    下面分析最关键的一个算法,match算法

      1  /**
      2      * Match a pattern agains a string and isolates wildcard replacement into
      3      * a <code>Stack</code>.
      4      * map,存储通配符每次匹配成功的值,这个map在include节点没有用,但是在action的配置中,使用,
      5      * @param map  The map to store matched values:详情请参考上一篇文章
      6      * @param data The string to match:要进行匹配的字符串
      7      * @param expr The compiled wildcard expression:已经转换成好的规则,是int型
      8      * @return True if a match:匹配结果
      9      * @throws NullPointerException If any parameters are null
     10      */
     11     public boolean match(Map<String, String> map, String data, int[] expr) {
     12         if (map == null) {
     13             throw new NullPointerException("No map provided");
     14         }
     15 
     16         if (data == null) {
     17             throw new NullPointerException("No data provided");
     18         }
     19 
     20         if (expr == null) {
     21             throw new NullPointerException("No pattern expression provided");
     22         }
     23         需要匹配的字符串的字符数组
     24         char[] buff = data.toCharArray();
     25 
     26         // Allocate the result buffer:分配可以匹配的结果集的数组
     27         char[] rslt = new char[expr.length + buff.length];
     28         表达式的字符的当前下标和上一个下标的值
     29         // The previous and current position of the expression character
     30         // (MATCH_*)
     31         int charpos = 0;
     32 
     33         // The position in the expression, input, translation and result arrays
     34         int exprpos = 0;表达式在数组中的下标
     35         int buffpos = 0;目标字符串的字符数组的下标
     36         int rsltpos = 0; 37         int offset = -1;偏移量
     38 
     39         // The matching count:
     40         int mcount = 0;
     41 
     42         // We want the complete data be in {0}
     43         map.put(Integer.toString(mcount), data);
     44         首先检查开始:匹配开始位置
     45         // First check for MATCH_BEGIN
     46         boolean matchBegin = false;
     47         MATCH_BEGIN代表开始位置,它的整数值为-4
     48         if (expr[charpos] == MATCH_BEGIN) {
     49             matchBegin = true;
     50             exprpos = ++charpos;表达式字符位置加1
     51         }
     52         寻找下一个特殊字符的位置(特殊字符是指在类中定义的五个静态常量,它们的值都小于0)
     53         // Search the fist expression character (except MATCH_BEGIN - already
     54         // skipped)
     55         while (expr[charpos] >= 0) {
     56             charpos++;下一个特殊字符的位置
     57         }
     58         当前的特殊字符
     59         // The expression charater (MATCH_*)
     60         int exprchr = expr[charpos];
     61         循环判断
     62         while (true) {
     63             // Check if the data in the expression array before the current
     64             // expression character matches the data in the input buffer
     65             if (matchBegin) {第一个判断执行:看下面对matchArray的分析
     66                 if (!matchArray(expr, exprpos, charpos, buff, buffpos)) {
     67                     return (false);
     68                 }
     69 
     70                 matchBegin = false;
     71             } else {
     72                 offset = indexOfArray(expr, exprpos, charpos, buff, buffpos);
     73 
     74                 if (offset < 0) {
     75                     return (false);
     76                 }
     77             }
     78             ?对于这个if判断,我一直存在一个疑问,上面的if执行了,那么,matchBegin变为false,假如上面的if不
     79             // Check for MATCH_BEGIN:执行,那么证明matchBegin本来就为false,下面的if肯定不会执行
     80             if (matchBegin) { 81                 if (offset != 0) {
     82                     return (false);
     83                 }
     84 
     85                 matchBegin = false;
     86             }
     87             将目标字符串的下标进行前移:举例:目标是aa/bb/cc/dd/ee,表达式为aa**cc**ee,经过上面的
    matchArray的计算,因为开始到第一个特殊字符,即第一个**,上面的matchArray需要判断的字符是aa,
    目标串aa和aa匹配成功,那么,要把目标字符串的位置前移,开始比较后面的字符 88 // Advance buffpos 89 buffpos += (charpos - exprpos); 90 检查是否结束标志位 91 // Check for END's 92 if (exprchr == MATCH_END) { 93 if (rsltpos > 0) { 94 map.put(Integer.toString(++mcount), 95 new String(rslt, 0, rsltpos)); 96 } 97 98 // Don't care about rest of input buffer 99 return (true); 100 } else if (exprchr == MATCH_THEEND) {?此处的用法尚未理解 101 if (rsltpos > 0) { 102 map.put(Integer.toString(++mcount), 103 new String(rslt, 0, rsltpos)); 104 } 105 106 // Check that we reach buffer's end 107 return (buffpos == buff.length); 108 } 109 寻找下一个特殊标志位 110 // Search the next expression character 111 exprpos = ++charpos; 112 进行位置的变化(例如位置1-5-7),现在1-5位置的字符已经验证通过,现在开始5-7,所有要进行开始位置 和结束位置的变换,之前的结束位置成为现在的开始位置 113 while (expr[charpos] >= 0) { 114 charpos++; 115 } 116 上一个字符 117 int prevchr = exprchr; 118 当前字符 119 exprchr = expr[charpos]; 120 判断之前的字符是路径匹配符还是文件匹配符 121 // We have here prevchr == * or **. 122 offset = 123 (prevchr == MATCH_FILE)如果是匹配文件,则使用indexOfArray,否则使用lastIndexOfArray 124 ? indexOfArray(expr, exprpos, charpos, buff, buffpos) 125 : lastIndexOfArray(expr, exprpos, charpos, buff, buffpos); 126 如果偏移量小于0,则证明未匹配成功 127 if (offset < 0) { 128 return (false); 129 } 130 131 // Copy the data from the source buffer into the result buffer 132 // to substitute the expression character 133 if (prevchr == MATCH_PATH) {匹配路径的(执行了上面的lastIndexOfArray方法) 134 while (buffpos < offset) {匹配成功,将目标字符串的位置向后移,进行后面的位置的字符的比较 135 rslt[rsltpos++] = buff[buffpos++]; 136 } 137 } else { 138 // Matching file, don't copy '/'匹配文件,执行了上面的indexOfArray方法 139 while (buffpos < offset) { 140 if (buff[buffpos] == '/') {匹配文件,不允许包含斜杠,因为*只代表匹配文件 141 return (false); 142 } 143 匹配成功将目标字符串的位置向后移,进行后面的位置的字符的比较 144 rslt[rsltpos++] = buff[buffpos++]; 145 } 146 } 147 map在struts2针对action进行通配符使用的时候,有用 148 map.put(Integer.toString(++mcount), new String(rslt, 0, rsltpos)); 149 rsltpos = 0;,注意,在这里我们将rsltpos进行了归0处理。 150 } 151 }

     下面介绍indexOfArray和lastIndexOfArray两个方法的使用:

    /**
         * Get the offset of a part of an int array within a char array. <br> This
         * method return the index in d of the first occurrence after dpos of that
         * part of array specified by r, starting at rpos and terminating at
         * rend.取得字符串的偏移量。
         *
         * @param r    The array containing the data that need to be matched in
         *             d.表达式
         * @param rpos The index of the first character in r to look for.比较的开始位置
         * @param rend The index of the last character in r to look for plus 1.比较的结束位置
         * @param d    The array of char that should contain a part of r.目标字符串
         * @param dpos The starting offset in d for the matching.目标字符串的开始位置
         * @return The offset in d of the part of r matched in d or -1 if that was
         *         not found.
         */
        protected int indexOfArray(int[] r, int rpos, int rend, char[] d, int dpos) {
            // Check if pos and len are legal
            if (rend < rpos) {
                throw new IllegalArgumentException("rend < rpos");
            }
    
            // If we need to match a zero length string return current dpos
            if (rend == rpos) {
                return (d.length); //?? dpos?
            }
    
            // If we need to match a 1 char length string do it simply
            if ((rend - rpos) == 1) {
                // Search for the specified character
                for (int x = dpos; x < d.length; x++) {
                    if (r[rpos] == d[x]) {
                        return (x);
                    }
                }
            }
            循环匹配:匹配文件名字
            // Main string matching loop. It gets executed if the characters to
            // match are less then the characters left in the d buffer
            while (((dpos + rend) - rpos) <= d.length) {
                // Set current startpoint in d
                int y = dpos;
    
                // Check every character in d for equity. If the string is matched
                // return dpos,循环进行匹配
                for (int x = rpos; x <= rend; x++) {
                    if (x == rend) {要匹配到最末的位置
                        return (dpos);
                    }
                    一发现不等的,直接向后移动
                    if (r[x] != d[y++]) {
                        break;
                    }
                }
    
                // Increase dpos to search for the same string at next offset
                dpos++;
            }
    
            // The remaining chars in d buffer were not enough or the string
            // wasn't matched
            return (-1);
        }

    lastIndexOfArray

     1  /**
     2      * Get the offset of a last occurance of an int array within a char array.
     3      * <br> This method return the index in d of the last occurrence after
     4      * dpos of that part of array specified by r, starting at rpos and
     5      * terminating at rend.
     6      *
     7      * @param r    The array containing the data that need to be matched in
     8      *             d.
     9      * @param rpos The index of the first character in r to look for.
    10      * @param rend The index of the last character in r to look for plus 1.
    11      * @param d    The array of char that should contain a part of r.
    12      * @param dpos The starting offset in d for the matching.
    13      * @return The offset in d of the last part of r matched in d or -1 if
    14      *         that was not found.
    15      */
    16     protected int lastIndexOfArray(int[] r, int rpos, int rend, char[] d,
    17         int dpos) {
    18         // Check if pos and len are legal
    19         if (rend < rpos) {
    20             throw new IllegalArgumentException("rend < rpos");
    21         }
    22 
    23         // If we need to match a zero length string return current dpos
    24         if (rend == rpos) {
    25             return (d.length); //?? dpos?
    26         }
    27 
    28         // If we need to match a 1 char length string do it simply
    29         if ((rend - rpos) == 1) {
    30             // Search for the specified character
    31             for (int x = d.length - 1; x > dpos; x--) {
    32                 if (r[rpos] == d[x]) {
    33                     return (x);
    34                 }
    35             }
    36         }
    37         从后面向前面进行查找匹配,寻找最后一个满足的,因为这个方式用来匹配路径的
    38         // Main string matching loop. It gets executed if the characters to
    39         // match are less then the characters left in the d buffer
    40         int l = d.length - (rend - rpos);
    41 
    42         while (l >= dpos) {
    43             // Set current startpoint in d
    44             int y = l;
    45 
    46             // Check every character in d for equity. If the string is matched
    47             // return dpos
    48             for (int x = rpos; x <= rend; x++) {
    49                 if (x == rend) {
    50                     return (l);
    51                 }
    52 
    53                 if (r[x] != d[y++]) {
    54                     break;
    55                 }
    56             }
    57 
    58             // Decrease l to search for the same string at next offset
    59             l--;
    60         }
    61 
    62         // The remaining chars in d buffer were not enough or the string
    63         // wasn't matched
    64         return (-1);
    65     }

    matchArray:用来在开始位置判断匹配的

     1  /**
     2      * Matches elements of array r from rpos to rend with array d, starting
     3      * from dpos. <br> This method return true if elements of array r from
     4      * rpos to rend equals elements of array d starting from dpos to
     5      * dpos+(rend-rpos).
     6      *
     7      * @param r    The array containing the data that need to be matched in
     8      *             d.
     9      * @param rpos The index of the first character in r to look for.
    10      * @param rend The index of the last character in r to look for.
    11      * @param d    The array of char that should start from a part of r.
    12      * @param dpos The starting offset in d for the matching.
    13      * @return true if array d starts from portion of array r.
    14      */
    15     protected boolean matchArray(int[] r, int rpos, int rend, char[] d, int dpos) {
    16         if ((d.length - dpos) < (rend - rpos)) {
    17             return (false);
    18         }
    19         如果开始匹配的位置小于表达式的开始位置和结束位置直接,返回,否则,逐一进行比较是否相等
    20         for (int i = rpos; i < rend; i++) {
    21             if (r[i] != d[dpos++]) {
    22                 return (false);
    23             }
    24         }
    25 
    26         return (true);
    27     }

    现在对上面的的方法运行进行一个模拟:

    表达式:a*d*g*.xml :它的整形数组为:-4,97,-1,100,-1,103,-1,46,120,109,108,-5,-4为字符开始标志位,-5为

    字符结束标志位,-1为匹配文件名称的标志位,

    目标字符串:abcdefghi.xml:它的整形数组为:97,98,99,100,101,102,103,104,105,46,120,109,108

    首先,会执行matchArray方法,其中各个参数分别为:表达式整形数组,1,2,目标字符串整形数组,0

    那么matchArray运行的时候,(d.length - dpos) < (rend - rpos)判断说明字符串从匹配开始的长度小于表达式中要匹配

    的长度,那么肯定的是失败的。表达式中的97和目标字符串中的97匹配成功,所以matchArray方法调用成功。

    那么接下来:会执行242行和243行的代码:

    1 // Advance buffpos
    2 buffpos += (charpos - exprpos);

    即开始位置匹配成功,表达式的位置向前移动,开始匹配后面的位置表达式

    接下来,会调用indexOfArray方法:

    其中各个参数分别为:表达式整形数组,3,5,目标字符串整形数组,2

    则我们发现我们匹配的字符串是bcd,其中执行到了d的时候,

    1  if (x == rend) {
    2        return (dpos);
    3   }

    假如我们的字符串是bdd,那么第一个发现的d还没有到了最末尾,即不满足上面的那个条件,所以还会继续的找,直到

    最后一个。

    关于路径的匹配是从后面向前面递推的,我们在这里不进行过多的介绍。

    --end

    I believe that we are who we choose to be. Nobody‘s going to come and save you, you‘ve got to save yourself. 我相信我们成为怎样的人是我们自己的选择。没有人会来拯救你,你必须要自己拯救自己。
  • 相关阅读:
    资料存储
    Django学习
    爬虫学习
    time()模块
    一些功能性小程序段
    装饰器
    函数的知识点(理解难点剖析)
    linux中的回收站机制,防止rm -rf 事件
    python--列表解析式 -- 知识整理
    python- 时间模块 datatime 知识整理
  • 原文地址:https://www.cnblogs.com/caroline/p/2991784.html
Copyright © 2011-2022 走看看