簡體   English   中英

Pythonic方法找到匹配負正則表達式的字符串中的最后位置

[英]Pythonic way to find the last position in a string matching a negative regex

在Python中,我嘗試在與給定模式匹配的任意字符串中找到最后一個位置,該模式被指定為負字符集正則表達式模式。 例如,使用字符串uiae1iuae200 ,並且不是數字的模式(Python中的正則表達式模式為[^0-9] ),我需要'8'('200'之前的'e'' ) 結果。

實現這一目標的最pythonic方法是什么?

因為在Python文檔中快速找到方法文檔和最適合的方法(由於方法文檔位於相應頁面中間的某個位置,如重新頁面中的re.search() ),有點棘手,最好方式我很快發現自己正在使用re.search() - 但是當前的形式必須是一種次優的方式:

import re
string = 'uiae1iuae200' # the string to investigate
len(string) - re.search(r'[^0-9]', string[::-1]).start()

我對此不滿意有兩個原因: - a)我需要在使用[::-1]之前反轉string ,並且 - b)我還需要反轉結果位置(從len(string)減去它因為以前扭轉了弦。

需要有更好的方法,甚至可能是re.search()的結果。

我知道re.search(...).end() over .start() ,但是re.search()似乎將結果拆分成組,為此我沒有快速找到一種不麻煩的方式來申請它到最后一個匹配的組。 如果不指定組, .start() .end()等似乎始終匹配第一個組,該組沒有關於最后一個匹配的位置信息。 但是,選擇組似乎首先要求將返回值臨時保存在變量中(這會阻止整齊的單行),因為我需要訪問有關選擇最后一個組然后選擇.end()來自這個群體。

你的pythonic解決方案是什么? 我認為pythonic比擁有最優化的運行時更重要。

更新

解決方案在角落情況下也應該起作用,例如123 (沒有與正則表達式匹配的位置),空字符串等。它不應該崩潰,例如因為選擇空列表的最后一個索引。 然而,即使我在問題中上面的丑陋答案需要不止一行,我想這可能是不可能的(僅僅因為需要檢查re.search()re.finditer()的返回值re.finditer()在處理之前)。 出於這個原因,我會接受pythonic多線解決方案。

您可以使用re.finditer提取所有匹配項的起始位置,並從列表中返回最后一個匹配項。 試試這個Python代碼:

import re
print([m.start(0) for m in re.finditer(r'\D', 'uiae1iuae200')][-1])

打印:

8

編輯:為了使解決方案更加優雅,以便在所有類型的輸入中正常運行,這里是更新的代碼。 現在解決方案分為兩行,因為如果列表為空則必須執行檢查,然后它將打印-1否則索引值:

import re

arr = ['', '123', 'uiae1iuae200', 'uiae1iuae200aaaaaaaa']

for s in arr:
    lst = [m.start() for m in re.finditer(r'\D', s)]
    print(s, '-->', lst[-1] if len(lst) > 0 else None)

打印以下內容,如果未找到此索引,則打印None而不是index:

 --> None
123 --> None
uiae1iuae200 --> 8
uiae1iuae200aaaaaaaa --> 19

編輯2:正如OP在他的帖子中所述, \\d只是我們開始的一個例子,因此我提出了一個解決方案來處理任何一般的正則表達式。 但是,如果這個問題必須只用\\d來實現,那么我可以提供一個更好的解決方案,根本不需要列表理解,並且可以通過使用更好的正則表達式來查找最后出現的非數字字符來輕松編寫並打印其位置。 我們可以使用.*(\\D)正則表達式查找最后一次出現的非數字,並使用以下Python代碼輕松打印其索引:

import re

arr = ['', '123', 'uiae1iuae200', 'uiae1iuae200aaaaaaaa']

for s in arr:
    m = re.match(r'.*(\D)', s)
    print(s, '-->', m.start(1) if m else None)

打印字符串及其對應的非數字字符索引,如果沒有找到則為None

 --> None
123 --> None
uiae1iuae200 --> 8
uiae1iuae200aaaaaaaa --> 19

正如您所看到的,此代碼不需要使用任何列表理解,並且更好,因為它只需通過一個正則表達式調用來match即可找到索引。

但是,如果OP確實意味着它使用任何一般的正則表達式模式編寫,那么我將需要使用理解的上述代碼。 我甚至可以把它寫成一個函數,可以將正則表達式(如\\d甚至是復雜的)作為參數,並動態生成傳遞正則表達式的負數並在代碼中使用它。 如果確實需要,請告訴我。

對我而言,你只想要一個匹配給定模式的最后一個位置(在這種情況下不是一個數字模式)。
這與pythonic一樣:

import re

string = 'uiae1iuae200'
pattern = r'[^0-9]'

match = re.match(fr'.*({pattern})', string)
print(match.end(1) - 1 if match else None)

輸出:

 8 

或者與函數完全相同,並且有更多測試用例:

import re


def last_match(pattern, string):
    match = re.match(fr'.*({pattern})', string)
    return match.end(1) - 1 if match else None


cases = [(r'[^0-9]', 'uiae1iuae200'), (r'[^0-9]', '123a'), (r'[^0-9]', '123'), (r'[^abc]', 'abcabc1abc'), (r'[^1]', '11eea11')]

for pattern, string in cases:
    print(f'{pattern}, {string}: {last_match(pattern, string)}')

輸出:

 [^0-9], uiae1iuae200: 8 [^0-9], 123a: 3 [^0-9], 123: None [^abc], abcabc1abc: 6 [^1], 11eea11: 4 

這看起來不像Pythonic,因為它不是單行,它使用range(len(foo)) ,但它非常簡單,可能效率不高。

def last_match(pattern, string):
    for i in range(1, len(string) + 1):
        substring = string[-i:]
        if re.match(pattern, substring):
            return len(string) - i

我們的想法是迭代string的后綴從最短到最長,並檢查它是否與pattern匹配。

由於我們從最后檢查,我們確信我們遇到的匹配模式的第一個子串是最后一個。

暫無
暫無

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

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