1. 概述

在这个教程中,我们将学习如何检查字符串是否包含非字母数字字符。这种功能在诸如密码强度检查、应用程序中限制特殊字符输入等场景中非常重要。当我们希望限制其仅用于特定语言脚本时,这个需求变得更有趣,这也是我们在这里尝试解决的问题。

2. 使用正则表达式

我们认为使用正则表达式是实现这一要求最灵活的方法。考虑一个简单的场景:应用只接受英文数字和字母字符。为此,我们可以使用正则表达式\[^a-zA-Z0-9\]来识别非字母数字字符:

public class NonAlphaNumRegexChecker {
    private static final Pattern PATTERN_NON_ALPHNUM_USASCII = Pattern.compile("[^a-zA-Z0-9]+");
    
    public static boolean isAlphanumeric(String str) {
        Matcher matcher = PATTERN_NON_ALPHNUM_USASCII.matcher(str);
        return matcher.find();
    }
}

但如果应用需要接受其他语言的字母,那么我们必须调整正则表达式,使其也涵盖Unicode字母和数字。更多详细信息,请参阅Javadocs中的“Unicode支持”部分:这里。这里我们使用了正则表达式的二进制属性类,如IsAlphabeticIsDigit

public class NonAlphaNumRegexChecker {
    private static final Pattern PATTERN_NON_ALPHNUM_ANYLANG = Pattern.compile("[^\\p{IsAlphabetic}\\p{IsDigit}]");
    
    public static boolean containsNonAlphanumeric(String input) {
        Matcher matcher = PATTERN_NON_ALPHNUM_ANYLANG.matcher(input);
        return matcher.find();
    }
}

现在考虑另一个场景,应用只接受特定的Unicode脚本中的字符,比如西里尔字母、格鲁吉亚字母或希腊字母。为了实现这种情况,正则表达式支持如IsCyrillicIsGreekIsGeorgian等Unicode脚本类。让我们看一个例子:

public class NonAlphaNumRegexChecker {
    public static boolean containsNonAlphanumeric(String input, String script) {
        String regexScriptClass = "\\p{" + "Is" + script + "}";
        Pattern pattern = Pattern.compile("[^" + regexScriptClass + "\\p{IsDigit}]"); //Binary properties
        Matcher matcher = pattern.matcher(input);
        return matcher.find();
    }
}

由于上述方法需要将语言脚本作为参数,每次调用时都需要编译模式,这可能会成为性能瓶颈。因此,我们可以缓存所有在枚举Character.UnicodeScripthttps://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Character.UnicodeScript.html)中提到的脚本对应的编译好的`Pattern`对象,并使用键`script`进行检索。

3. 使用Character类的isLetterOrDigit()方法

现在,让我们来看看Character类的isLetterOrDigit()方法,它可以帮助实现上一节讨论的所有用例。第一个解决方案通过isLetterOrDigit()方法检查任何语言字符串中的非字母数字字符

public class NonAlphaNumericChecker {
    public static boolean isNonAlphanumericAnyLangScript(String str) {
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            if (!Character.isLetterOrDigit(c)) {
                return true;
            }
        }
        return false;
    }
}

但是,如果我们只想允许特定语言脚本,我们需要稍作调整。这里,我们把一个字符视为非字母数字字符,当它既不是那种语言的字母也不是数字时:

public class NonAlphaNumericChecker {
    public static boolean isNonAlphanumericInLangScript(String str, String script) {
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            if (!Character.UnicodeScript.of(c).toString().equalsIgnoreCase(script)
              && !Character.isDigit(c)) {
                return true;
            }
        }
        return false;
    }
}

4. 使用Apache Commons Lang库的StringUtils

这是目前为止所有技术中最不灵活的一种。StringUtils类的isAlphanumeric()方法支持所有Unicode字母或数字,但没有提供识别字符串中使用的语言脚本的支持。让我们看看它是如何工作的:

public static boolean isNonAlphanumericAnyLangScriptV2(String str) {
    return !StringUtils.isAlphanumeric(str);
}

5. 总结

在这个教程中,我们讨论了一些必须检查字符串中是否存在非字母数字字符的场景。结论是,正则表达式技术是所有可用选项中最灵活的。这里使用的代码片段,以及相关的JUnit测试用例,可以在GitHub上找到。