java字符串星号、问号匹配问题

问题场景

  • 我在做Java Web项目的时候,遇到一个问题,需要对一些请求路径进行过滤。例如对/test/*下的请求不需要进行session验证(会跳转到login页面登录)。 因此我打算写一个方法来完成这个功能。

功能拟定

  • 编写一个方法,包含三个参数,分别为:
    1. 源字符串:source,需要被匹配的任意字符串。
    2. 匹配表达式:regex,类似于正则表达式,包含问号和星号等特殊符号。
    3. 是否区分大小写:ignoreCase(一个可选功能)
  • 需要判断source是否能被regex匹配,其中regex中的星号代表0到多个任意字符,问号代表任意一个字符,当要匹配source中的问号、星号、反斜杠时,需要在regex中加入反斜杠进行转义(即如果要匹配*、?、\,regex需要加上反斜杠,如\*,\?,\\)。

编码如下

  • 代码的重点思路都写在注释里了,我测试了大部分用例,基本没有bug(不保证复杂情况下的bug,如有的话希望看到的人能评论一下进行修正)。
  • 基本的思路如下:
    1.首先进行非空判断,这个是最基本的,顺便过滤一下多余的星号,由于对正则不是非常的熟悉,去除多余星号那个正则让我鼓捣了半天,如果有更好的方法望评论指教。
    2.遍历匹配表达式的同时与源表达式进行比对,出现星号则进行递归运算。
    3.扫描完成无误返回true,否则返回false。
/**
 * @author miaoch
 */
public class StringUtils {

    public static boolean isLike(String source, String regex) {
        return isLike(source, regex, false);
    }

    /**
     * 判断source字符串是否能够被regex匹配,能满足普通情况,没有考虑特别复杂的情况
     * @param source 任意字符串
     * @param regex 包含*或?的匹配表达式(如果要匹配*、?、\,需要加上反斜杠,如\*,\?,\\)
     * @param ignoreCase 大小写敏感
     * @return
     */
    public static boolean isLike(String source, String regex, boolean ignoreCase) {
        if (source == null || regex == null) return false;
        if (ignoreCase) {
            source = source.toLowerCase();
            regex = regex.toLowerCase();
        }
        return matches(source, regex.replaceAll("(^|([^\\\\]))[\\*]{2,}", "$2*"));//去除多余*号
    }

    private static boolean matches(String source, String regex) {
        //如果source与regex完全相等,且source不包含反斜杠,则返回true。(当source包含*号或者?号,此时亦满足,不做多余判断)
        if (source.equals(regex) && source.indexOf('\\') < 0) return true;
        int rIdx = 0, sIdx = 0;//同时遍历源字符串与匹配表达式
        while (rIdx < regex.length() && sIdx < source.length()) {
            char c = regex.charAt(rIdx);//以匹配表达式为主导
            switch (c) {
                case '*'://匹配到*号进入下一层递归
                    String tempSource = source.substring(sIdx);//去除前面已经完全匹配的前缀
                    String tempRegex = regex.substring(rIdx + 1);//从星号后一位开始认为是新的匹配表达式
                    for (int j = 0; j <= tempSource.length(); j++) {//此处等号不能缺,如(ABCD,*),等号能达成("", *)条件
                        if (matches(tempSource.substring(j), tempRegex)) {//很普通的递归思路
                            return true;
                        }
                    }
                    return false;//排除所有潜在可能性,则返回false
                case '?':
                    break;
                case '\\'://匹配到反斜杠跳过一位,匹配下一个字符串
                    c = regex.charAt(++rIdx);
                default:
                    if (source.charAt(sIdx) != c) return false;//普通字符的匹配
            }
            rIdx++;
            sIdx++;
        }
        //最终source被匹配完全,而regex也被匹配完整或只剩一个*号
        return source.length() == sIdx &&
                (regex.length() == rIdx || 
                    regex.length() == rIdx + 1 && regex.charAt(rIdx) == '*');
    }

    public static void main(String args[]) {
        System.out.println("str=ABCD regex=ABC? :"+isLike("ABCD", "ABC?"));
        System.out.println("str=ABCD regex=A??? :"+isLike("ABCD", "A???"));
        System.out.println("str=ABCD regex=A?? :"+isLike("ABCD", "A??"));
        System.out.println("str=ABCD regex=?BC? :"+isLike("ABCD", "?BC?"));
        System.out.println("str=ABCD regex=*B*D :"+isLike("ABCD", "*B*D"));
        System.out.println("str=ABCD regex=*BCD :"+isLike("ABCD", "*BCD"));
        System.out.println("str=ABCD regex=*A*B*D :"+isLike("ABCD", "*A*B*D"));       
        System.out.println("str=ABCD regex=A* :"+isLike("ABCD", "A*"));       
        System.out.println("str=A?BCD regex=A\\?* :"+isLike("A?BCD", "A\\?*"));       
        System.out.println("str=? regex=\\? :"+isLike("?", "\\?"));   
        System.out.println("str=\\? regex=\\? :"+isLike("\\?", "\\?"));//这个只匹配问号,而源字符串为反斜杠问号   
        System.out.println("str=\\? regex=\\\\\\? :"+isLike("\\?", "\\\\\\?"));
    }

    //借用正则表达式(投机取巧)。
    //不建议使用,如要使用还得更正。需要去除regex中一些字符的特殊含义
    //如'.','?','=','[',']','?','^','$','{','}',',',':','!','-','+'等
    //由于情况复杂,不做过多的更正
    public static boolean isLike2(String source, String regex) {
        if (source == null || regex == null) return false;
        StringBuilder newRegex = new StringBuilder();
        for (int i = 0; i < regex.length(); i++) {
            char c = regex.charAt(i);
            switch (c) {
                case '*':
                    newRegex.append(".*");
                    break;
                case '?':
                    newRegex.append(".");
                    break;
                case '\\':
                    newRegex.append("\\");
                    c = regex.charAt(++i);
                default:
                    newRegex.append(c);
            }
        }
        return source.matches(newRegex.toString());
    }
}

备注

  • 文章题目和测试用例都是抄的,但是内容完全是自己写的,毫无借鉴。
©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值