负环视正则表达式-仅出现一次-Java
我试图找出一个字符串是否只包含一个单词的出现处 e、 g 字符串中的任何位置都可能出现多个负环视正则表达式-仅出现一次-Java,java,regex,string-matching,Java,Regex,String Matching,我试图找出一个字符串是否只包含一个单词的出现处 e、 g 字符串中的任何位置都可能出现多个foo,因此它们可以是非连续的 我使用字符串foobarfoo在java中测试以下正则表达式匹配,但它不起作用,并且返回true: static boolean testRegEx(String str){ return str.matches(".*(foo)(?!.*foo).*"); } 我知道这个主题看起来可能重复,但我很惊讶,因为当我使用这个正则表达式时:(foo)(?!*foo)。*它
foo
,因此它们可以是非连续的
我使用字符串foobarfoo
在java中测试以下正则表达式匹配,但它不起作用,并且返回true:
static boolean testRegEx(String str){
return str.matches(".*(foo)(?!.*foo).*");
}
我知道这个主题看起来可能重复,但我很惊讶,因为当我使用这个正则表达式时:(foo)(?!*foo)。*
它可以工作
知道为什么会这样吗 您可以使用此模式:
^(?>[^f]++|f(?!oo))*foo(?>[^f]++|f(?!oo))*$
它有点长,但性能很好
与ashdflasd字符串的经典示例相同:
详情:
(?> # open an atomic group
[^f]++ # all characters but f, one or more times (possessive)
| # OR
f(?!oo) # f not followed by oo
)* # close the group, zero or more times
++
就像贪婪的量词++
,但不允许回溯
(?>…)
类似于非捕获组(?:…)
,但也不允许回溯
这些功能用于性能(内存和速度),但子模式可替换为:
(?:[^f]+|f(?!oo))*
如果您想检查一个字符串是否包含另一个字符串,这里有两个可能的解决方案(一个带regex,一个不带regex) 它们都很好用。下面是您的示例演示:
String str1 = "jjdhfoobarfoo";
String str2 = "wewwfobarfoo";
String str3 = "jjfffoobarfo";
String foo = "foo";
System.out.println(containsOnlyOnce(str1, foo)); // false
System.out.println(containsOnlyOnce(str2, foo)); // true
System.out.println(containsOnlyOnce(str3, foo)); // true
System.out.println(containsRegexOnlyOnce(str1, foo)); // false
System.out.println(containsRegexOnlyOnce(str2, foo)); // true
System.out.println(containsRegexOnlyOnce(str3, foo)); // true
有人回答了问题,但删除了它 以下短代码工作正常:
static boolean testRegEx(String str){
return !str.matches("(.*?foo.*){0}|(.*?foo.*){2,}");
}
你知道如何在正则表达式内部反转结果吗 正则表达式的问题是,第一个
*
最初消耗整个字符串,然后后退,直到找到一个与其余正则表达式匹配的位置。这意味着,如果字符串中有多个foo
,您的正则表达式将始终与最后一个匹配。从这一立场出发,前瞻也将永远成功
用于验证的正则表达式必须比用于匹配的正则表达式更精确。您的正则表达式失败,因为*
可以匹配哨兵字符串“foo”。您需要在尝试匹配的foo
之前和之后主动阻止匹配。展示了一种方法;还有一个:
"^(?>(?!foo).)*+foo(?>(?!foo).)*+$"
虽然效率不高,但我认为它更容易阅读。事实上,您可能会使用以下正则表达式:
"^(?!.*foo.*foo).+$"
它的效率要低得多,但是一个完整的regexn00b可能会知道它的功能
最后,请注意,这些正则表达式——我的或Casimir的——都没有使用lookbehind。我知道这似乎是做这项工作的完美工具,但事实上,回头看永远不应该是你想要的第一个工具。而且不仅仅是在Java中。无论您使用什么regex风格,以正常方式匹配整个字符串几乎总是比使用lookbehinds更容易。而且通常效率也更高。使用两个固定式外观头:
static boolean testRegEx(String str){
return str.matches("^(?=.*foo)(?!.*foo.*foo.*$).*");
}
有两个关键点是,有一个消极的前瞻性检查,检查是否有2个foo's锚定到start,并且重要的是包含输入结束。第二个正则表达式与第一个输入字符串匹配,这就是所发生的情况。虽然,对于第二个输入示例,它将返回
false
。但通常字符串可能不会以foo
开头。现在问题已编辑,foo
可能发生在字符串中的任何位置,而另一个foo
则是Too。因此,您的意思是,对于更长的模式,没有通用的方法?就像对于LJKASHDFLASDFKJHASDFLKJHASDLKJHASDLKJHASDLFJK
你不能做同样的事情,对吗?请注意,foo
只是一个例子here@ArianHosseinzadeh:您可以使用所需的字符串执行此操作。您所需要的只是拆分第一个字母上的字符串以动态组合您的模式。请您详细说明一下++
是什么?你为什么不在任何地方使用“.*”?这个{0}
是干什么的?如果你这么想的话,它不会阻止与foo
匹配。事实上,它基本上把第一个替代方案变成了一个不可操作的选项。{0}
有合法的用途,但这不是其中之一。至于反转正则表达式,您可以将其包装为一个负的前瞻,但我不建议这样做:^(?!(?:(.*foo.*){0}|(.*foo.*){2,})$。+$
"^(?>(?!foo).)*+foo(?>(?!foo).)*+$"
"^(?!.*foo.*foo).+$"
static boolean testRegEx(String str){
return str.matches("^(?=.*foo)(?!.*foo.*foo.*$).*");
}