3

我的困境如下:我正在尝试将外部文件中的字符串与互斥正则表达式匹配(即一个字符串不能匹配多个正则表达式

您会建议什么算法让我将给定的字符串与保证不会与其他用例相交的 RegEx 匹配?

该程序在语法上是有效的,但是它存在重叠。文件中有 2.5m 行。

我正在考虑对文件中的每一行进行标记,然后为每个条件设置标志(因此,如果“x”包含 [AZ]+ 设置大写标志)

  • 正则表达式必须检查是否存在:
    • 标点
    • 大写字母
    • 小写字母
    • 整数

可能的用例:

U = Upper-case letter L = Lower-case letter P = Punctuation N = Number

---- null
U--- [A-Z]+
UL-- [A-Za-z]+
U-N- [A-Z0-9]+
ULN- [A-Za-z0-9]+
ULNP [\\p{Punct}\\sA-Za-z0-9]+
-L-- [a-z]+
-LN- [a-z0-9]+
--N- [0-9]
---P [\\p{Punct}\\s]+
U--P [\\p{Punct}\\sA-Z]+
-L-P [\\p{Punct}\\sa-z]+
--NP [\\p{Punct}\\s0-9]+
UL-P [\\p{Punct}\\sA-Za-z]+
U-NP [\\p{Punct}\\sA-Z0-9]+
ULNP [\\p{Punct}\\sA-Za-z0-9]+

到目前为止我所拥有的(效率低下,RegEx 重叠)

public static void main(String[] args) {
File file = new File("/home/tyler/workspace/PasswordAnalyzer/docs/test.txt");

try {
    Scanner scan = new Scanner(file);
    while (scan.hasNextLine()) {
        String s = scan.nextLine();
        /*****************************************
        * Evaluate password Strings using RegExs
        ******************************************/
        if(s.matches("[A-Z0-9]+")){
            //Upper-case & numeric

        } else if(s.matches("[a-z0-9]+")){
            //Lower-case & numeric

        } else if(s.matches("[A-Za-z0-9]+")){
            //Alphanumeric

        } else if(s.matches("[A-Za-z]+")){
            //Upper-case & lower-case

        } else if(s.matches("[0-9]+")){
            //Numeric

        } else if(s.matches("[A-Z]+")){
            //Upper-case

        }  else if(s.matches("[a-z]+")){
            //Lower-case

        } else if(s.matches("[\\p{Punct}\\s]+")){
            //Punctuation

        } else if(s.matches("[\\p{Punct}\\sA-Z]+")){
            //Punctuation & upper-case

        } else if(s.matches("[\\p{Punct}\\sa-z]+")){
            //Punctuation & lower-case

        } else if(s.matches("[\\p{Punct}\\s0-9]+")){
            //Punctuation & numeric

        } else if(s.matches("[\\p{Punct}\\sA-Za-z]+")){
            //Punctuation & alphabetical

        } else if(s.matches("[\\p{Punct}\\sA-Z0-9]+")){  
            //Punctuation & upper-case & numeric

        } else if(s.matches("[\\p{Punct}\\sa-z0-9]+")){
            //Punctuation & lower-case & numeric

        } else if(s.matches("[\\p{Punct}\\sA-Za-z0-9]+")){
            //Punctuation & alphanumeric

        } else {
            System.err.println("ERROR: unhandled RegEx");
        } 
    } //loop
} catch (FileNotFoundException fnfe){
    System.err.println(fnfe.getMessage());
}

}//main()

修订:为 4 种可能的条件(大写、小写、数字、标点符号)设置标志,动态生成相应变量的名称,并相应递增。想法?

(main() 的底部)

public static void main(String[] args) {
File file = new File("/home/tyler/workspace/PasswordAnalyzer/docs/test.txt");
Analyzer a = new Analyzer(); //used by Java reflections object

try {
    Scanner scan = new Scanner(file);
    while (scan.hasNextLine()) {
        String s = scan.nextLine();
        //Flags
        boolean U_flag = false;
        boolean L_flag = false;
        boolean N_flag = false;
        boolean P_flag = false;

        for(int i=0; i<s.length(); i++){
            String c = s.substring(i, i);
            /*****************************************
             * Set flags (U,L,N,P)
             ****************************************/
            //U_flag (upper-case)
            if(c.matches("[A-Z]+")){
                U_flag = true;
            }
            //L_flag (lower-case)
            if(c.matches("[a-z]+")){
                L_flag = true;
            }
            //N_flag (numeric)
            if(c.matches("[0-9]+")){
                N_flag = true;
            }
            //P_flag (punctuation)
            if(c.matches("[\\p{Punct}\\s]+")){
                P_flag = true;
            }
            /*****************************************
             * Identify corresponding counter variable
             ****************************************/
            String dest = "";

            //U_flag
            if(U_flag){dest.concat("U");
            } else {dest.concat("_");}

            //L_flag
            if(L_flag){dest.concat("L");
            } else {dest.concat("_");}

            //N_flag
            if(N_flag){dest.concat("N");
            } else {dest.concat("_");}

            //P_flag
            if(P_flag){dest.concat("P");}

            //increment variable stored in dest (Java reflections?)

        }//for-loop
    } //while-loop
} catch (FileNotFoundException fnfe){
    System.err.println(fnfe.getMessage());
}

}//main()
4

2 回答 2

4

就目前而言,你有很多重叠。例如,

U--- [A-Z]+
UL-- [A-Za-z]+
U-N- [A-Z0-9]+
ULN- [A-Za-z0-9]+
ULNP [\\p{Punct}\\sA-Za-z0-9]+

任何被第一个正则表达式匹配的字符串也将被任何后续表达式匹配。

如果我正确地解释了您的问题,那么您正在尝试通过它包含的不同字符类来表征每个输入字符串。例如,字符串ABCDE被描述为U---, while Ab9b8is ULN-

为此,您所要做的就是(伪代码):

for (String s in allStrings)
{
    int charClass = 0
    for (Char c in s.characters)
    {
        case c
            when upper-case: 
                charClas |= 8
                break;
            when lower-case: 
                charClas |= 4
                break;
            when numeric: 
                charClas |= 2
                break;                
            when punctuation: 
                charClas |= 1
                break;
    }
    // do something with charClass
}

在“做某事”注释中charClass,作为位字符串的 的值将包含您的ULNP值。要将其转换为包含U、和的文字字符串L,您需要设置一个字符串数组NP

String[] ulnpStrings = { "----","---P","--N-","--NP","-L--", "-L-P",... etc };

然后使用 的值charClass作为该数组的索引。要计算出现次数,请对数组执行相同操作

int[] ulnpCounts = new int[16];

并在每次迭代时根据 charClass 的值增加元素,因此

    ...
    // do something with charClass
    unlpCounts[charClass]++
}
for (int i=0; i<unlpStrings.length; i++)
{
    System.out.printf("%s %6d\n",unlpStrings[i],unlpCounts[i]);
}
于 2012-11-17T22:52:09.973 回答
0

试试这个:

Pattern p = Pattern.compile("^(?:([A-Z]+)|([a-z]+)|([0-9]+)|([\\p{Punct}\\s]+)|.)*$");
Matcher m = p.matcher("");
Scanner sc = new Scanner(new File("test.txt"));
while (sc.hasNextLine())
{
  String line = sc.nextLine();
  StringBuilder sb = new StringBuilder();
  if (m.reset(line).matches())
  {
    sb.append(m.start(1) > -1 ? "U" : "-");
    sb.append(m.start(2) > -1 ? "L" : "-");
    sb.append(m.start(3) > -1 ? "N" : "-");
    sb.append(m.start(4) > -1 ? "P" : "-");
  }
  System.out.printf("%s : %s%n", sb.toString(), line);
}

样本输出:

U--- : AAAA
-L-- : aaaa
--N- : 2222
---P : %%%%
UL-- : AAAa
U-N- : AAA2
U--P : AAA%

每种类型的字符都分配有自己的捕获组。该start(int)方法告诉了该组的最新捕获开始的位置;值-1表示该组未参加比赛。(您也可以使用m.group(int) != null.)

我没有对失败的比赛做任何事情,因为我不知道你想如何处理它。另外,请注意最后一个分支中的点匹配其他分支不匹配的任何内容,包括非 ASCII 字母、数字等。如果您想支持其他字符集,则必须使用 Unicode 类别,例如\p{Lu}\p{Ll}, ETC..

于 2012-11-18T00:09:38.327 回答