Java:对2.5M字符串使用互斥的RegExs(15)的有效方法?

提问

我的困境如下:我试图将外部文件中的字符串与互斥的正则表达式进行匹配(即,字符串不能与多个RegExs匹配)

您建议采用什么算法使我能够将给定的String与RegEx匹配,以确保不会与其他用例相交?

该程序在语法上是有效的,但实际上存在重叠.文件中有250万行.

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

  • Regular expressions must check for the presence of:
    • Punctuation
    • Upper-case letters
    • Lower-case letters
    • Integers

可能的用例:

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()

最佳答案

就目前而言,您有很多重叠之处.例如,

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 —,而Ab9b8被描述为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,N和P的文字字符串,您需要设置一个字符串数组

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]);
}
评论