Java函数返回字符串是否包含非法字符

21

我有一些字符希望被视为“非法”:

~, #, @, *, +, %, {, }, <, >, [, ], |, , , \, _, ^

我想编写一个检查字符串并确定该字符串是否包含这些非法字符的方法(true/false):

public boolean containsIllegals(String toExamine) {
    return toExamine.matches("^.*[~#@*+%{}<>[]|\"\\_^].*$");
}

然而,简单的matches(...)检查并不适用于此。我需要这种方法扫描字符串中的每个字符,并确保它不是这些字符之一。当然,我可以做一些可怕的事情,比如:

public boolean containsIllegals(String toExamine) {
    for(int i = 0; i < toExamine.length(); i++) {
        char c = toExamine.charAt(i);

        if(c == '~')
            return true;
        else if(c == '#')
            return true;

        // etc...
    }
}

有没有更优雅/高效的方法来完成这个任务?


5
为什么使用“matches”不可行? - Peter Elliott
因为那不够酷! - thang
6个回答

29
你可以利用PatternMatcher类来完成。你可以将所有过滤的字符放入字符类中,然后使用Matcher#find()方法检查模式在字符串中是否可用。
你可以这样做:-
public boolean containsIllegals(String toExamine) {
    Pattern pattern = Pattern.compile("[~#@*+%{}<>\\[\\]|\"\\_^]");
    Matcher matcher = pattern.matcher(toExamine);
    return matcher.find();
}

find() 方法会在字符串中查找给定的模式,只要找到一次就会返回 true。


另外一个还未被提出的方法是使用 String#split(regex)。我们可以按照给定的模式来分割字符串,并检查数组的长度。如果长度为 1,那么该模式不在字符串中。

public boolean containsIllegals(String toExamine) {
    String[] arr = toExamine.split("[~#@*+%{}<>\\[\\]|\"\\_^]", 2);
    return arr.length > 1;
}

如果 arr.length > 1 ,那就意味着字符串中包含了模式中的一个字符,这就是为什么它被分割的原因。我已经将 limit = 2 作为第二个参数传递给 split,因为我们只需要单个分割。


最干净和最好解释的。 - IAmYourFaja
对于那些在细节上跟随并且会被绊倒的人,请注意,使用 "matcher.find" 时您只需使用括号内的较短正则表达式,而使用 "matcher.matches" 时您将使用较长的表达式。 只要为每个技术使用正确的正则表达式,两种技术都可以通过我的单元测试。 - Ted

13

我需要一种方法来扫描字符串中的每个字符

如果你必须一个字符一个字符地进行扫描,正则表达式可能不是一个好的选择。然而,由于在您的“黑名单”上的所有字符代码都小于128,因此您可以使用一个小的布尔数组来实现:

static final boolean blacklist[] = new boolean[128];

static {
    // Unassigned elements of the array are set to false
    blacklist[(int)'~'] = true;
    blacklist[(int)'#'] = true;
    blacklist[(int)'@'] = true;
    blacklist[(int)'*'] = true;
    blacklist[(int)'+'] = true;
    ...
}

static isBad(char ch) {
    return (ch < 128) && blacklist[(int)ch];
}

如果我没有记错,在Java中未初始化的布尔值会变成false,对吧? - 11684
@11684 正确 - boolean 数组的元素最初为 false - Sergey Kalinichenko
1
也许这可以作为答案的一部分,以便初学者也能理解? - 11684

10

使用常量来避免在每次验证时重新编译正则表达式。

private static final Pattern INVALID_CHARS_PATTERN = 
                               Pattern.compile("^.*[~#@*+%{}<>\\[\\]|\"\\_].*$");

并将您的代码更改为:

public boolean containsIllegals(String toExamine) {
    return INVALID_CHARS_PATTERN.matcher(toExamine).matches();
}

这是使用正则表达式最高效的方法。


8
如果您无法使用匹配器,则可以像这样做,这比一堆不同的if语句或字节数组更清晰。
 for(int i = 0; i < toExamine.length(); i++) {
    char c = toExamine.charAt(i);
    if("~#@*+%{}<>[]|\"_^".contains(c)){
         return true;
    }
 }

5
尝试否定包含所有黑名单字符的字符类:
public boolean containsIllegals(String toExamine) {
    return toExamine.matches("[^~#@*+%{}<>\\[\\]|\"\\_^]*");
}

如果字符串包含非法字符,这将返回true(您的原始函数在这种情况下似乎会返回false)。

方括号 [ 右侧紧挨着插入符号 ^,表示反转该字符集。请注意,在 String.matches() 中,您不需要锚定符号^$,因为它会自动匹配整个字符串。


2

一个相对简洁的方法是使用 String.replaceAll 方法:

public boolean containsIllegal(final String toExamine) {
    return toExamine.length() != toExamine.replaceAll(
            "[~#@*+%{}<>\\[\\]|\"\\_^]", "").length();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接