簡體   English   中英

如何在python中構建基於標記化正則表達式的迭代器

[英]How do I build a tokenizing regex based iterator in python

我將這個問題基於對另一個SO問題的回答 ,這是使用more_itertools的成對迭代器配方對基於標記的正則表達式迭代器的 具體嘗試

以下是我從該答案中獲取的代碼:

from more_itertools import pairwise
import re

string = "dasdha hasud hasuid hsuia dhsuai dhasiu dhaui d"
# split according to the given delimiter including segments beginning at the beginning and ending at the end
for prev, curr in pairwise(re.finditer(r"^|[ ]+|$", string)):
    print(string[prev.end(): curr.start()])  # originally I yield here

然后,我注意到,如果字符串以分隔符開始或結束(例如string = " dasdha hasud hasuid hsuia dhsuai dhasiu dhaui d " ),則標記生成器將在開頭打印空字符串(實際上是字符串開頭和字符串結尾的額外匹配項)以及其令牌輸出列表的末尾,因此,為了解決這個問題,我嘗試了以下(非常難看)其他正則表達式的嘗試:

  1. “(?:^ | [] | $)+” -這似乎很簡單,好像它應該可以工作,但是由於某種原因,它不會(但在其他正則表達式引擎上的行為也大相徑庭) ,因此無法構建從字符串開頭的單個匹配項及其后的分隔符 ,字符串開頭以某種方式還會消耗其后的字符! (這也是我看到的與其他引擎不同的地方,這是一個BUG嗎?還是與特殊的非有形字符和我不知道的python中的or(|)運算符有關?),此解決方案一旦匹配了分隔符,然后又對字符串結尾($)字符本身進行了另一次匹配,則對包含字符串結尾的雙精度匹配也沒有任何作用。

  2. “(?:[] | $ | ^)+” -首先放置定界符實際上解決了一個問題,開始處的拆分不包含字符串開頭(但我對此並不在意, '對標記本身很感興趣),當字符串的開頭沒有定界符但字符串的末尾仍然存在問題時,它也匹配字符串的開頭。

  3. “((^ [] *)|([] * $)|([] +)” -最終嘗試使字符串開始成為第一個匹配項的一部分(在第一個匹配項中實際上並沒有太大的問題位置),但盡我所能,我無法擺脫定界符+末尾然后出現定界符匹配問題(這會產生一個額外的空字符串),但是,我仍向您展示此示例(帶有分組),因為它顯示了末尾特殊字符$被匹配兩次,一次是與前面的定界符匹配,一次是單獨匹配(2組2匹配)。

我的問題是:

  1. 為什么我在嘗試#1時得到如此奇怪的行為
  2. 如何解決字符串結尾問題?
  3. 我是坦克嗎,也就是說,有一種簡單的方法可以解決我盲目失蹤的問題嗎?
  4. 請記住,解決方案不能更改字符串,並且必須產生一個可迭代的生成器 ,該生成器在標記之間的空間迭代,而不是在標記本身之間進行迭代 (這最后一部分似乎不必要地使答案復雜化,因為否則我會有一個簡單的答案,但是如果您必須知道( 如果您不做進一步的閱讀 )這是我正在構建的一個更大框架的一部分,該生成方法由管道繼承,然后該管道以各種模式從中構造出產生語句,用於從中提取字段半結構化分類器驅動的消息)

您遇到的問題是由於零寬度匹配的棘手性和未記錄的邊緣情況造成的。 您可以通過使用否定的環視解決方案來解決它們,以明確告訴Python如果字符串的開頭或結尾有定界符,則不要為^$生成匹配項:

delimiter_re = r'[\n\- ]'     # newline, hyphen, or space
search_regex = r'''^(?!{0})   # string start with no delimiter
                   |          # or
                   {0}+       # sequence of delimiters (at least one)
                   |          # or
                   (?<!{0})$  # string end with no delimiter
                '''.format(delimiter_re)
search_pattern = re.compile(search_regex, re.VERBOSE)

請注意,這將在一個空字符串中生成一個匹配項,而不是零,並且不會將開始和結束匹配項分開。

遍歷非定界符序列並使用結果匹配項來查找所需的字符串組件可能更簡單:

token = re.compile(r'[^\n\- ]+')
previous_end = 0
for match in token.finditer(string):
    do_something_with(string[previous_end:match.start()])
    previous_end = match.end()
do_something_with(string[previous_end:])

在字符串末尾得到的額外匹配項是因為在末尾匹配了定界符序列之后,正則表達式引擎再次在末尾查找匹配項,並找到了$的零寬度匹配項。

您在^|...模式的字符串開頭得到的行為比較棘手:正則表達式引擎在字符串開頭看到^的零寬度匹配並發出它,而沒有嘗試其他|字符| 備擇方案。 在零寬度匹配之后,引擎需要避免再次產生該匹配,以免產生無限循環。 這個特定的引擎似乎是通過跳過字符來做到這一點的,但是詳細信息未記錄在案,並且源代碼也很難導航。 如果您想閱讀,這是源代碼的一部分。

您在(?:^|...)+模式的字符串開頭所得到的行為更加棘手。 直接執行此操作,引擎將在字符串開頭尋找(?:^|...)的匹配項,找到^ ,然后尋找另一個匹配項,再次尋找^ ,然后無限期尋找另一個匹配項。 有一些未記錄的處理使它永遠無法運行,並且這種處理似乎產生了零寬度匹配,但是我不知道該處理是什么。

聽起來您只是想返回所有“單詞”的列表,並以任意數量的排字字符分隔。 相反,您可以只使用正則表達式組和否定正則表達式^實現此目的:

# match any number of consecutive non-delim chars
string = "  dasdha hasud hasuid hsuia dhsuai dhasiu dhaui d  "
delimiters = '\n\- '
regex = r'([^{0}]+)'.format(delimiters)
for match in re.finditer(regex, string):
    print(match.group(0))

輸出:

dasdha
hasud
hasuid
hsuia
dhsuai
dhasiu
dhaui
d

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM