了解量词

时间:2017-01-31 11:41:44

标签: java regex quantifiers

我正在浏览Java Tutorial on Quantifiers

贪婪,不情愿和占有量词之间的差异有所不同。

我无法完全理解其中的区别。

解释如下:

Enter your regex: .*foo  // greedy quantifier
Enter input string to search: xfooxxxxxxfoo
I found the text "xfooxxxxxxfoo" starting at index 0 and ending at index 13.

Enter your regex: .*?foo  // reluctant quantifier
Enter input string to search: xfooxxxxxxfoo
I found the text "xfoo" starting at index 0 and ending at index 4.
I found the text "xxxxxxfoo" starting at index 4 and ending at index 13.

Enter your regex: .*+foo // possessive quantifier
Enter input string to search: xfooxxxxxxfoo
No match found.

第一个例子使用贪婪量词。*来找到"任何",零次或多次,然后是字母" f" " O" " O&#34 ;.因为量词是贪婪的,所以表达式的。*部分首先会占用整个输入字符串。此时,整体表达式不能成功,因为已经消耗了最后三个字母(" f"" o"" o")。因此,匹配器一次缓慢地退回一个字母,直到最右边出现" foo"已经反刍,此时比赛成功,搜索结束。

然而,第二个例子是不情愿的,所以它首先消费"没什么"。因为" foo"没有出现在字符串的开头,它被迫吞下第一个字母(" x"),这会触发第一个匹配0和4.我们的测试工具继续直到输入字符串用尽的过程。它在4和13找到另一场比赛。

第三个例子找不到匹配,因为量词是占有性的。在这种情况下,整个输入字符串被。* +消耗,不留任何东西来满足" foo"在表达的最后。使用占有量词来表示你想要抓住所有东西而不会退缩的情况;在没有立即找到匹配的情况下,它将胜过等效的贪心量词。

2 个答案:

答案 0 :(得分:10)

懒惰(不情愿)&的主要区别贪婪的情况是,回溯结构的行为,以及所有格的情况也是如此激进

  • 懒惰的情况总是在一次匹配后,将匹配引擎的焦点放在正则表达式中的下一个运算符上。如果下一个运算符失败,则回溯结构将强制重复惰性情况,并且这将继续,直到运算符或目标文本结束;例如,在你的例子中,在每次成功匹配后传递给char f的匹配,所以每当你有一个foo短语时,你就会得到一个匹配,这就是为什么我们从它的用法中得到多个匹配
  

。*?foo

  的 X fooxxxxxxfoo
  在开始时,懒惰的情况将与x成功匹配(在成功的空匹配之后)并将焦点传递给下一个运算符; foo正则表达式的x部分,因为它出现在foo之后,我们得到了这个片段的匹配,对于字符串的次要部分也是一样的。

  • 贪婪的情况是相反的,将继续匹配直到失败,永远不会将焦点传递给下一个运算符,只有当匹配失败时,回溯才会生效,并且下一个运算符将是从反向匹配;
  

。* foo

  xfooxxxxxxfo的 0   
当贪婪的情况在此时(最后一个字符)时,匹配将失败,因为我们无法匹配正则表达式的backtrace部分。比回溯将迫使贪婪的案件foo执行其步骤并强制执行下一个操作员foo,类似于懒惰案件;

  xfooxxxxxx的 ˚F OO
  此时,backtracking部分将获得成功匹配,从而以整个字符串的成功匹配结束。

  • 占有案例与贪婪案件非常相似,除了导致backtrack的匹配失败的最后一部分,这不是占有的情况。如果它可以匹配,它将拥有并将在此过程中牺牲比赛的成功。如果它在匹配字符时失败,那么焦点就会传递给正则表达式的下一个运算符。
  

。* + foo

  xfooxxxxxxfo的 0   
类似于贪婪的情况,我们已到达字符串的末尾,但是占有情况仍然可以匹配它,因此不会将火炬传递给 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_aplication_settings" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#e2e2e2" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.smok.maps.AplicationSettings"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true"> <TextView android:id="@+id/textSync" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#000000" android:text="@string/sync" /> <LinearLayout android:id="@+id/line1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/textSync"> <TextView android:id="@+id/actual" android:layout_width="wrap_content" android:textColor="#000000" android:layout_height="wrap_content" android:text="@string/actual" /> <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/count" /> <TextView android:id="@+id/actual2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#000000" android:text="min" /> </LinearLayout> <LinearLayout android:id="@+id/lin2" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/line1" android:orientation="horizontal"> <CheckBox android:id="@+id/checkBox1" android:layout_width="50dp" android:layout_height="50dp" android:button="@xml/setting_checkbox" android:focusable="false" android:gravity="center" /> <ImageView android:layout_width="wrap_content" android:layout_height="match_parent" android:src="@drawable/point" /> <TextView android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center" android:textColor="#000000" android:text="@string/show_choose_point" /> </LinearLayout> <LinearLayout android:id="@+id/lin3" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/lin2" android:orientation="horizontal"> <CheckBox android:id="@+id/checkBox2" android:layout_width="50dp" android:layout_height="50dp" android:button="@xml/setting_checkbox" android:focusable="false" android:gravity="center" /> <ImageView android:layout_width="wrap_content" android:layout_height="match_parent" android:src="@drawable/sync" /> <TextView android:layout_width="wrap_content" android:layout_height="match_parent" android:textColor="#000000" android:gravity="center" android:text="@string/sync_request" /> </LinearLayout> <LinearLayout android:id="@+id/lin4" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/lin3" android:orientation="horizontal"> <CheckBox android:id="@+id/checkBox3" android:layout_width="50dp" android:layout_height="50dp" android:button="@xml/setting_checkbox" android:focusable="false" android:gravity="center" /> <ImageView android:layout_width="wrap_content" android:layout_height="match_parent" android:src="@drawable/show_chooce" /> <TextView android:layout_width="wrap_content" android:layout_height="match_parent" android:textColor="#000000" android:gravity="center" android:text="@string/show_choosen_points" /> </LinearLayout> <LinearLayout android:id="@+id/lin5" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/lin4" android:orientation="horizontal"> <CheckBox android:id="@+id/checkBox4" android:layout_width="50dp" android:layout_height="50dp" android:button="@xml/setting_checkbox" android:focusable="false" android:gravity="center" /> <ImageView android:layout_width="wrap_content" android:layout_height="match_parent" android:src="@drawable/position2" /> <TextView android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center" android:textColor="#000000" android:text="@string/current_location" /> </LinearLayout> </RelativeLayout> <LinearLayout android:id="@+id/LinearLayout1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:orientation="horizontal"> <Button android:id="@+id/back_bt" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:textColor="#ffffff" android:background="@xml/roundedbutton" android:text="@string/back" /> <View android:layout_width="0dp" android:layout_weight="0.1" android:layout_height="match_parent"> </View> <Button android:id="@+id/confirm_bt" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:textColor="#ffffff" android:background="@xml/roundedbutton" android:text="@string/save" /> </LinearLayout> </RelativeLayout> 结构,并且会导致匹配失败。 / p>

答案 1 :(得分:7)

一般规则

理解量词?*+(分别为“零或一”,“零或更多”,“一个或多个”)的基本知识

  • 我们说量词是贪心,如果它试图详细说明尽可能多的字符。
  • 我们说量词是不情愿懒惰),如果它试图尽可能少地填写字符。
  • 我们说量词是占有欲如果它是贪婪的并且不允许回溯。

只有了解正则表达式解析器的工作方式时,才能理解“回溯”的含义(参见下面的“动态示例”)。

单例解释

  • ?:先测试1次,然后是0;如果你发现了1次,那么你需要丢弃它,你可以这样做
  • ??:首先测试0次,然后测试1次
  • ?+:先测试1次,然后是0;如果你发现了一次,然后你需要丢弃它,你不能这样做
  • *:尝试尽可能多地出现(甚至为0);如果你发现N次出现然后你需要丢弃(部分)它们,你可以从最后一次开始
  • *?:尝试尽可能减少出现次数(即使为0)
  • *+:尝试尽可能多地出现(甚至为0);如果您发现了N次,然后您需要丢弃(部分)它们,那么不能这样做
  • +:尝试尽可能多地出现(至少1次);如果你发现N次出现然后你需要丢弃(部分)它们,你可以从最后一次开始
  • +?:尝试减少发生次数(至少1次)
  • ++:尝试尽可能多地出现(至少1次);如果您发现了N次,然后您需要丢弃(部分)它们,那么不能这样做

动态示例

在本节中,我将尝试向您展示正则表达式解析器背后的逻辑:

1)案例/.*foo/

首先是转向子模式/.*/。它开始详细说明第一个字符:

xfooxxxxxxfoo
^

然后它试图详细说明尽可能多的字符:

xfooxxxxxxfoo
^^
xfooxxxxxxfoo
^^^
[...]
xfooxxxxxxfoo
^^^^^^^^^^^
xfooxxxxxxfoo
^^^^^^^^^^^^
xfooxxxxxxfoo
^^^^^^^^^^^^^

光标到达结尾,但子模式/foo/尚未发挥作用。因此,光标“返回”以允许子模式/foo/获得匹配:

xfooxxxxxxfoo
^^^^^^^^^^^^

/foo/仍无法获得匹配,因此我们需要再次返回:

xfooxxxxxxfoo
^^^^^^^^^^^
xfooxxxxxxfoo
^^^^^^^^^^

现在子模式/foo/可以匹配:

xfooxxxxxxfoo
^^^^^^^^^^^^^

所以匹配是整个字符串xfooxxxxxxfoo

2)案例/.*?foo/

首先是转向子模式/.*?/。它是懒惰的,所以我们喜欢它匹配0个字符。但如果确实如此,子模式/foo/无法获得匹配,因此必须详细说明一个字符:

xfooxxxxxxfoo
^

现在轮到foo了:

xfooxxxxxxfoo
^^^^

所以匹配为xfoo

(如果为正则表达式设置类型global,那么解析器将从匹配后的第一个字符重新启动,给出第二个匹配xxxxxxfoo

3)案例/.*+foo/

首先是转向子模式/.*+/。它试图详细说明尽可能多的字符:

xfooxxxxxxfoo
^
[...]
xfooxxxxxxfoo
^^^^^^^^^^^^^

光标到达结尾,但子模式/foo/尚未发挥作用。所以光标“回头”......哦不,多么可惜,它不能(因为它占有欲)!

所以我们没有比赛。