如何使用正则表达式检查字符串是否包含受限制的单词?

时间:2013-09-11 18:20:06

标签: java regex

这些是我不应该在我的地址中允许的字符串:

"PO BOX","P0 DRAWER","POSTOFFICE", " PO ", " BOX ",
 "C/O","C.O."," ICO "," C/O "," C\0 ","C/0","P O BOX",
 "P 0 BOX","P 0 B0X","P0 B0X","P0 BOX","P0BOX","P0B0X",
 "POBX","P0BX","POBOX","P.0.","P.O","P O "," P 0 ",
 "P.O.BOX","P.O.B","POB ","P0B","P 0 B","P O B",
 " CARE ","IN CARE"," APO "," CPO "," UPO ", "GENDEL",
 "GEN DEL", "GENDELIVERY","GEN DELIVERY","GENERALDEL",
 "GENERAL DEL","GENERALDELIVERY","GENERAL DELIVERY"

我创建了正则表达式:此表达式仅验证POBOx部分 - 请更正我的地址字段中不允许上述所有字符串

"([\\w\\s*\\W]*((P(O|OST)?.?\\s*((O(FF(ICE)?)?)?.?\\s*(B(IN|OX|.?))|B(IN|OX))+))[\\w\\s*\\W]*)+
|([\\w\\s*\\W]* (IN \s*(CARE)?\\s*)|\s*[\\w\\s*\\W]*((.?(APO)?|.?(cPO)?|.?(uPO))?.?\s*) [\\w\\s*\\W]*|([\\w\\s*\\W]*(GEN(ERAL)?)?.?\s*(DEL(IVERY)?)?.?\s* [\\w\\s*\\W]*))";

3 个答案:

答案 0 :(得分:2)

我猜你在试图查看地址字符串是否包含任何受限制的短语。

请不要在一个正则表达式中执行此操作。

执行一个单一的大规模正则表达式匹配查询意味着很难理解你为创建正则表达式所做的工作,如果弹出更多限制而难以扩展,并且通常不是良好的代码实践。


这是一个(希望)更理智的方法:

public static final String RESTRICTIONS[] = { " P[0O] ", " B[0O]X ", "etc, etc" };

public static boolean containsRestrictions(String testString) {
    for (String expression : RESTRICTIONS) {
        Matcher restriction = Pattern.compile(expression).matcher(testString);
        if (restriction.find())
            return true;
    }
    return false;
}

你仍然在进行正则表达式匹配,所以你可以把你喜欢的schmancy正则表达式放到你的限制列表中,但它也适用于普通的旧字符串。现在,您只需要验证每个单独的正则表达式是否有效,而不是针对所有可能的情况验证巨型正则表达式。如果您想添加新限制,只需将其添加到列表中即可。如果您真的很喜欢,可以从配置文件加载限制或使用spring注入它,这样您讨厌的产品人员可以添加地址限制而无需触及代码。


编辑:为了使这更容易阅读,并做你真正想要的事情(限制字符串与其他字符串分开使用空格),你可以完全从限制中删除正则表达式并做一些基本的在你的方法中匹配工作。

// No regexes here, just words you wanna restrict
public static final String RESTRICTIONS[] = { "PO", "PO BOX", "etc, etc" };

public static boolean containsRestrictions(String testString) {
    for (String word : RESTRICTIONS) {
        String expression = "(^|\\s)" + word + "(\\s|$)";
        Matcher restriction = Pattern.compile(expression).matcher(testString);
        if (restriction.find())
            return true;
    }
    return false;
}

答案 1 :(得分:1)

那么,你想像专业人士一样搜索子串吗?我建议使用Aho Corasick algorithm解决你遇到的问题。

卖点:

  

它是一种字典匹配算法,用于在输入文本中定位有限字符串集(“字典”)的元素。它同时匹配所有模式。

幸运的是,存在Java实现。你可以得到它here

以下是如何使用它:

// this is the part you have to do only once

AhoCorasick tree = new AhoCorasick(); 

String[] terms = {"PO BOX","P0 DRAWER",...};

for (int i = 0; i < terms.length; i++) {
     tree.add(terms[i].getBytes(), terms[i]); 
}
tree.prepare();



// here comes the part you use for every address you want to check

String text = "The ga3 mutant of Arabidopsis is a gibberellin-responsive. In UPO, that is...";

boolean restrictedWordFound = false;

@SuppressWarnings("unchecked")
Iterator<SearchResult> search = (Iterator<SearchResult>)tree.search(text.getBytes());

if(search.hasNext()) {
    restrictedWordFound = true;
}

如果找到匹配项,restrictedWordFound将为真。

注意:此搜索区分大小写。由于你的字符串都是大写的,我建议你先用临时大写变体转换地址并使用匹配。这样,您将涵盖所有可能的组合。

根据我的测试,Aho Corasick比基于正则表达式的搜索更快,并且在大多数情况下比使用contains和其他基于String的方法的天真字符串搜索更快。您可以添加更多过滤词; Aho Corasick是要走的路。

答案 2 :(得分:0)

您可以声明:正则表达式:

,而不是使用这种复杂的正则表达式
"PO BOX|P0 DRAWER|POSTOFFICE| PO | BOX |C/O|C.O.| ICO | C/O | C\0 |C/0|P O BOX|P 0 BOX|P 0 B0X|P0 B0X|P0 BOX|P0BOX|P0B0X|POBX|P0BX|POBOX|P.0.|P.O|P O | P 0 |P.O.BOX|P.O.B|POB |P0B|P 0 B|P O B| CARE |IN CARE| APO | CPO | UPO |GENDEL|GEN DEL|GENDELIVERY|GEN DELIVERY|GENERALDEL|GENERAL DEL|GENERALDELIVERY|GENERAL DELIVERY"

否定答案。

编译正则表达式(在Java中)时,生成的机制将变得更加高效。 (Java使用DFA最小化)。