受{0}量词是否确实有意义的问题的启发,我开始玩一些包含{0}
量词的正则表达式,并编写了这个小Java程序,该程序只是根据各种测试正则表达式拆分测试短语:
private static final String TEST_STR =
"Just a test-phrase!! 1.2.3.. @ {(t·e·s·t)}";
private static void test(final String pattern) {
System.out.format("%-17s", "\"" + pattern + "\":");
System.out.println(Arrays.toString(TEST_STR.split(pattern)));
}
public static void main(String[] args) {
test("");
test("{0}");
test(".{0}");
test("([^.]{0})?+");
test("(?!a){0}");
test("(?!a).{0}");
test("(?!.{0}).{0}");
test(".{0}(?<!a)");
test(".{0}(?<!.{0})");
}
==>输出:
"": [, J, u, s, t, , a, , t, e, s, t, -, p, h, r, a, s, e, !, !, , 1, ., 2, ., 3, ., ., , @, , {, (, t, ·, e, ·, s, ·, t, ), }]
"{0}": [, J, u, s, t, , a, , t, e, s, t, -, p, h, r, a, s, e, !, !, , 1, ., 2, ., 3, ., ., , @, , {, (, t, ·, e, ·, s, ·, t, ), }]
".{0}": [, J, u, s, t, , a, , t, e, s, t, -, p, h, r, a, s, e, !, !, , 1, ., 2, ., 3, ., ., , @, , {, (, t, ·, e, ·, s, ·, t, ), }]
"([^.]{0})?+": [, J, u, s, t, , a, , t, e, s, t, -, p, h, r, a, s, e, !, !, , 1, ., 2, ., 3, ., ., , @, , {, (, t, ·, e, ·, s, ·, t, ), }]
"(?!a){0}": [, J, u, s, t, , a, , t, e, s, t, -, p, h, r, a, s, e, !, !, , 1, ., 2, ., 3, ., ., , @, , {, (, t, ·, e, ·, s, ·, t, ), }]
"(?!a).{0}": [, J, u, s, t, a, , t, e, s, t, -, p, h, ra, s, e, !, !, , 1, ., 2, ., 3, ., ., , @, , {, (, t, ·, e, ·, s, ·, t, ), }]
"(?!.{0}).{0}": [Just a test-phrase!! 1.2.3.. @ {(t·e·s·t)}]
".{0}(?<!a)": [, J, u, s, t, , a , t, e, s, t, -, p, h, r, as, e, !, !, , 1, ., 2, ., 3, ., ., , @, , {, (, t, ·, e, ·, s, ·, t, ), }]
".{0}(?<!.{0})": [Just a test-phrase!! 1.2.3.. @ {(t·e·s·t)}]
以下内容不足为奇:
""
,".{0}"
和"([^.]{0})?+"
刚才的每一个字符之前分开,这使得因0量词感。"(?!.{0}).{0}"
而且".{0}(?<!.{0})"
什么都不匹配 对我来说很有意义:0量化令牌的负向超前/向后看不会匹配。是什么让我感到惊讶:
"{0}"
&"(?!a){0}"
:实际上我在这里期望一个Exception,因为前面的令牌是不可量化的:因为前面没有{0}
任何内容,(?!a){0}
而且实际上不是负数。两者都在每个字符之前都匹配,为什么?如果我在javascript验证器中尝试该正则表达式,则会收到“无法量化的错误”,请参阅此处的演示!该正则表达式在Java和Javascript中的处理方式是否不同?"(?!a).{0}"
&".{0}(?<!a)"
:这里也有个小小的惊喜:这些匹配在词组的每个字符之前,但在a
。之前/之后。我的理解是,在(?!a).{0}
“ (?!a)
否定前瞻”部分中断言不可能a
从字面上进行匹配,但我正在向前看.{0}
。我以为它不能与0量化的令牌一起使用,但是看起来我也可以将Lookahead与它们一起使用。==>因此,对我而言,剩下的奥秘是为什么(?!a){0}
实际上在我的测试短语中的每个字符之前都进行匹配。那真的不应该是一个无效的模式并抛出PatternSyntaxException或类似的东西吗?
更新:
如果我在Android Activity中运行相同的Java代码,结果将有所不同!正则表达式(?!a){0}
确实确实会引发PatternSyntaxException,请参阅:
03-20 22:43:31.941: D/AndroidRuntime(2799): Shutting down VM
03-20 22:43:31.950: E/AndroidRuntime(2799): FATAL EXCEPTION: main
03-20 22:43:31.950: E/AndroidRuntime(2799): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.appham.courseraapp1/com.appham.courseraapp1.MainActivity}: java.util.regex.PatternSyntaxException: Syntax error in regexp pattern near index 6:
03-20 22:43:31.950: E/AndroidRuntime(2799): (?!a){0}
03-20 22:43:31.950: E/AndroidRuntime(2799): ^
03-20 22:43:31.950: E/AndroidRuntime(2799): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2180)
03-20 22:43:31.950: E/AndroidRuntime(2799): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2230)
03-20 22:43:31.950: E/AndroidRuntime(2799): at android.app.ActivityThread.access$600(ActivityThread.java:141)
03-20 22:43:31.950: E/AndroidRuntime(2799): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1234)
03-20 22:43:31.950: E/AndroidRuntime(2799): at android.os.Handler.dispatchMessage(Handler.java:99)
03-20 22:43:31.950: E/AndroidRuntime(2799): at android.os.Looper.loop(Looper.java:137)
03-20 22:43:31.950: E/AndroidRuntime(2799): at android.app.ActivityThread.main(ActivityThread.java:5041)
03-20 22:43:31.950: E/AndroidRuntime(2799): at java.lang.reflect.Method.invokeNative(Native Method)
03-20 22:43:31.950: E/AndroidRuntime(2799): at java.lang.reflect.Method.invoke(Method.java:511)
03-20 22:43:31.950: E/AndroidRuntime(2799): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
03-20 22:43:31.950: E/AndroidRuntime(2799): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
03-20 22:43:31.950: E/AndroidRuntime(2799): at dalvik.system.NativeStart.main(Native Method)
03-20 22:43:31.950: E/AndroidRuntime(2799): Caused by: java.util.regex.PatternSyntaxException: Syntax error in regexp pattern near index 6:
03-20 22:43:31.950: E/AndroidRuntime(2799): (?!a){0}
03-20 22:43:31.950: E/AndroidRuntime(2799): ^
03-20 22:43:31.950: E/AndroidRuntime(2799): at java.util.regex.Pattern.compileImpl(Native Method)
03-20 22:43:31.950: E/AndroidRuntime(2799): at java.util.regex.Pattern.compile(Pattern.java:407)
03-20 22:43:31.950: E/AndroidRuntime(2799): at java.util.regex.Pattern.<init>(Pattern.java:390)
03-20 22:43:31.950: E/AndroidRuntime(2799): at java.util.regex.Pattern.compile(Pattern.java:381)
03-20 22:43:31.950: E/AndroidRuntime(2799): at java.lang.String.split(String.java:1832)
03-20 22:43:31.950: E/AndroidRuntime(2799): at java.lang.String.split(String.java:1813)
03-20 22:43:31.950: E/AndroidRuntime(2799): at com.appham.courseraapp1.MainActivity.onCreate(MainActivity.java:22)
03-20 22:43:31.950: E/AndroidRuntime(2799): at android.app.Activity.performCreate(Activity.java:5104)
03-20 22:43:31.950: E/AndroidRuntime(2799): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1080)
03-20 22:43:31.950: E/AndroidRuntime(2799): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2144)
03-20 22:43:31.950: E/AndroidRuntime(2799): ... 11 more
为什么Android中的regex的行为不同于普通Java?
我做了一些调查Oracle Java 1.7的源。
"{0}"
我发现一些在主循环中找到?,*或+时抛出“悬挂元字符”的代码。也就是说,不是紧接在某些文字,组"."
或显式检查量词的其他位置之后。由于某种原因,{
不在该列表中。结果是,它经过所有对特殊字符的检查,并开始分析文字字符串。它遇到的第一个字符是{
,它告诉解析器是时候停止解析文字字符串并检查量词了。
结果是"{n}"
将匹配空字符串n次。
另一个结果是,一秒钟"x{m}{n}"
将首先匹配x
m
时间,然后匹配空字符串n
时间,实际上忽略了{n}
@Kobi在上面的评论中提到的。
对我来说似乎是一个错误,但是如果他们想保持它的向后兼容性,也不会令我感到惊讶。
"(?!a){0}"
"(?!a)"
只是一个可量化的节点。您可以检查下一个字符是否为'a'10次。不过,每次都会返回相同的结果,因此它不是很有用。在我们的例子中,它将检查下一个字符是否为'a'0次,这将始终成功。
请注意,作为匹配长度为0的优化(例如此处),量词从不贪婪。这也可以防止"(?!a)*"
情况下的无限递归。
"(?!a).{0}" & ".{0}(?<!a)"
如上所述,{0}
执行检查0次,始终成功。它实际上忽略了之前发生的任何事情。这意味着"(?!a).{0}"
与相同"(?!a)"
,具有预期的结果。
另一个相似。
Android与众不同
如@GenericJam所述,android是不同的实现,在这些极端情况下可能具有不同的特性。我也尝试查看该源,但android实际上在那儿使用本机代码:)
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句