Java正则表达式:AZ和-或_,但只能出现一次

bom*_*bax -2 java regex

我只涉足正则表达式,想知道是否有人可以帮助我制作一个 Java 正则表达式,它与具有以下特性的字符串相匹配:

  1. 长度为1-14个字符
  2. 它仅由 AZ、az 和字母 _ 或 - 组成
  3. 符号 - 和 _ 只能(一起)包含一次,并且不能出现在开头

它应该匹配

  • 再一次问好
  • 这是有效的
  • 还有这个_

但不是

  • -无效
  • 不允许这样做
  • Nor-This_thing
  • 确实非常非常长的字符串

我尝试过以下正则表达式字符串

[a-zA-Z^\\-_]+[\\-_]?[a-zA-Z^\\-_]*
Run Code Online (Sandbox Code Playgroud)

这似乎有效。但是,我不确定如何用这种方法来完成总字符限制部分。我也尝试过

[[a-zA-Z]+[\\-_]?[a-zA-Z]*]{1,14}
Run Code Online (Sandbox Code Playgroud)

但它匹配(例如) abc-cde_aa ,但它不应该匹配。

Bor*_*der 5

这应该有效:

(?![_-])(?!(?:.*[_-]){2,})[A-Za-z_-]{1,14}
Run Code Online (Sandbox Code Playgroud)

正则表达式非常复杂,让我尝试解释一下。

  • (?![_-])负前瞻。从字符串的开头断言第一个字符不是_or -。负向先行“查看”当前位置并检查它是否与包含和[_-]的字符组不匹配。_-
  • (?!(?:.*[_-]){2,})另一个否定的前瞻,这次匹配(?:.*[_-]){2,}是一个非捕获组,重复至少两次。组是.*[_-],它是任何字符,后跟与之前相同的组。所以我们希望看到某些字符出现一次_-多次。
  • [A-Za-z_-]{1,14}是简单的一点。它只是说出该组中的字符[A-Za-z_-]1 到 14 次。

该模式的第二部分是最棘手的,但却是一个非常常见的技巧。如果您希望看到某个字符A在模式中的某个点至少重复几次,那么您希望至少X看到该模式多次,因为您必须有.*AX

zzzzAzzzzAzzzzA....
Run Code Online (Sandbox Code Playgroud)

你不在乎还有什么。所以你得到的是(.*A){X,}. 现在,您不需要捕获该组 - 这只会减慢引擎的速度。所以我们让该组成为非捕获 - (?:.*A){X,}

您所拥有的是您只想看到该模式一次,因此您希望发现该模式重复两次或多次。因此,它插入负前瞻。

这是一个测试用例:

public static void main(String[] args) {
    final String pattern = "(?![_-])(?!(?:.*[_-]){2,})[A-Za-z_-]{1,14}";
    final String[] tests = {
            "Hello-Again",
            "ThisIsValid",
            "AlsoThis_",
            "_NotThis_",
            "-notvalid",
            "Not-Allow-This",
            "Nor-This_thing",
            "VeryVeryLongStringIndeed",
    };
    for (final String test : tests) {
        System.out.println(test.matches(pattern));
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

true
true
true
false
false
false
false
false
Run Code Online (Sandbox Code Playgroud)

注意事项:

  1. 该字符-在字符组中是特殊的。它必须位于组的开头或结尾,否则它指定一个范围
  2. 环顾四周是很棘手的,而且常常违反直觉。它将在不消耗的情况下检查匹配,允许您在同一数据上测试多个条件。
  3. 重复量词{}非常有用。它有 3 个状态。{X}被精确地重复了X几次。{X,}至少重复多次X。并且在和之间{X, Y}重复。XY