您可以使用前瞻和反向引用来解决此问题。但请注意,现在您至少需要 2 个字符。起始字母和另一个(由于+
)。您可能想要这样做,+
以便*
第二个字符类可以重复 0 次或更多次:
^(?!.*(.)\1)[a-zA-Z][a-zA-Z\d._-]*$
前瞻是如何工作的?首先,这是一个消极的前瞻性。如果内部的模式找到匹配项,则前瞻会导致整个模式失败,反之亦然。因此,如果我们确实有两个连续的字符,我们可以在里面有一个匹配的模式。首先,我们在字符串 ( ) 中查找任意位置.*
,然后匹配单个(任意)字符 ( .
) 并用括号捕获它。因此,该角色进入捕获组1
。然后我们要求这个捕获组跟在它后面(用 引用它\1
)。所以内部模式将尝试在字符串中的每个位置(由于回溯) 是否有一个字符跟在它自己后面。如果找到这两个连续字符,则该模式将失败。如果找不到它们,引擎会跳回到前瞻开始的位置(字符串的开头)并继续匹配实际模式。
或者,您可以将其拆分为两个单独的检查。一个用于有效字符和起始字母:
^[a-zA-Z][a-zA-Z\d._-]*$
一个用于连续字符(您可以在其中反转匹配结果):
(.)\1
这将大大提高您的代码的可读性(因为它比前瞻更不晦涩),它还可以让您检测模式中的实际问题并返回适当且有用的错误消息。