[英]Parse 4th capital letter of line in Python?
如何解析大寫字母第4次出現的文本行? 例如給出以下行:
adsgasdlkgasYasdgjaUUalsdkjgaZsdalkjgalsdkjTlaksdjfgasdkgj
oiwuewHsajlkjfasNasldjgalskjgasdIasdllksjdgaPlsdakjfsldgjQ
我想抓住:
`ZsdalkjgalsdkjTlaksdjfgasdkgj`
`PlsdakjfsldgjQ`
我確信有可能比正則表達式更好的方式,但我試圖做一個非貪婪的比賽; 這樣的事情:
match = re.search(r'[A-Z].*?$', line).group()
我提出兩種方法。
方法1:全力以赴的正則表達式
In [1]: import re
In [2]: s = 'adsgasdlkgasYasdgjaUUalsdkjgaZsdalkjgalsdkjTlaksdjfgasdkgj'
In [3]: re.match(r'(?:.*?[A-Z]){3}.*?([A-Z].*)', s).group(1)
Out[3]: 'ZsdalkjgalsdkjTlaksdjfgasdkgj'
.*?[AZ]
消費字符,包括第一個大寫字母。
(?:
... ){3}
重復上述三次而不創建任何捕獲組。
以下.*?
匹配第四個大寫字母前的剩余字符。
最后, ([AZ].*)
捕獲第四個大寫字母以及隨后進入捕獲組的所有內容。
方法2:更簡單的正則表達式
In [1]: import re
In [2]: s = 'adsgasdlkgasYasdgjaUUalsdkjgaZsdalkjgalsdkjTlaksdjfgasdkgj'
In [3]: ''.join(re.findall(r'[A-Z][^A-Z]*', s)[3:])
Out[3]: 'ZsdalkjgalsdkjTlaksdjfgasdkgj'
這會直接攻擊問題,我認為更容易閱讀。
無論如何不使用正則表達式將被視為過於冗長 - 雖然在bytcodelevel它是一個非常簡單的算法運行,因此輕量級。
可能是regexps更快,因為它們是用本機代碼實現的,但“一種顯而易見的方法”雖然很無聊,但在可讀性方面肯定勝過任何合適的正則表達式:
def find_capital(string, n=4):
count = 0
for index, letter in enumerate(string):
# The boolean value counts as 0 for False or 1 for True
count += letter.isupper()
if count == n:
return string[index:]
return ""
通過使用正則表達式拆分字符串,然后切片結果列表,發現這個更簡單:
import re
text = ["adsgasdlkgasYasdgjaUUalsdkjgaZsdalkjgalsdkjTlaksdjfgasdkgj",
"oiwuewHsajlkjfasNasldjgalskjgasdIasdllksjdgaPlsdakjfsldgjQ"]
for t in text:
print "".join(re.split("([A-Z])", t, maxsplit=4)[7:])
方便的是,如果沒有足夠的大寫字母,這會給你一個空字符串。
一個不錯的單線解決方案可能是:
>>> s1 = 'adsgasdlkgasYasdgjaUUalsdkjgaZsdalkjgalsdkjTlaksdjfgasdkgj'
>>> s2 = 'oiwuewHsajlkjfasNasldjgalskjgasdIasdllksjdgaPlsdakjfsldgjQ'
>>> s1[list(re.finditer('[A-Z]', s1))[3].start():]
'ZsdalkjgalsdkjTlaksdjfgasdkgj'
>>> s2[list(re.finditer('[A-Z]', s2))[3].start():]
'PlsdakjfsldgjQ'
為什么會這樣(只有一行)?
re.finditer('[AZ]', s1)
[3]
.start()
s1[position:]
獲取我們需要的部分s1[position:]
我相信這對你有用,並且在未來很容易擴展:
check = 'adsgasdlkgasYasdgjaUUalsdkjgaZsdalkjgalsdkjTlaksdjfgasdkgj'
print re.match('([^A-Z]*[A-Z]){3}[^A-Z]*([A-Z].*)', check ).group(2)
正則表達式的第一部分([^AZ]*[AZ]){3}
是真正的密鑰,它找到前三個大寫字母並將它們與它們之間的字符一起存儲在組1中,然后我們跳過任何數字在第三個大寫字母后面的非大寫字母,最后,我們捕獲字符串的其余部分。
測試各種方法。 我原來寫了string_after_Nth_upper
並沒有發布; 看到jsbueno的方法是相似的; 除了對每個字符(甚至是小寫字母)進行加法/計數比較之外,他的方法稍慢。
s='adsasdlkgasYasdgjaUUalsdkjgaZsdalkjgalsdkjTlaksdjfgasdkgj'
import re
def string_after_Nth_upper(your_str, N=4):
upper_count = 0
for i, c in enumerate(your_str):
if c.isupper():
upper_count += 1
if upper_count == N:
return your_str[i:]
return ""
def find_capital(string, n=4):
count = 0
for index, letter in enumerate(string):
# The boolean value counts as 0 for False or 1 for True
count += letter.isupper()
if count == n:
return string[index:]
return ""
def regex1(s):
return re.match(r'(?:.*?[A-Z]){3}.*?([A-Z].*)', s).group(1)
def regex2(s):
return re.match(r'([^A-Z]*[A-Z]){3}[^A-Z]*([A-Z].*)', s).group(2)
def regex3(s):
return s[list(re.finditer('[A-Z]', s))[3].start():]
if __name__ == '__main__':
from timeit import Timer
t_simple = Timer("string_after_Nth_upper(s)", "from __main__ import s, string_after_Nth_upper")
print 'simple:', t_simple.timeit()
t_jsbueno = Timer("find_capital(s)", "from __main__ import s, find_capital")
print 'jsbueno:', t_jsbueno.timeit()
t_regex1 = Timer("regex1(s)", "from __main__ import s, regex1; import re")
print "Regex1:",t_regex1.timeit()
t_regex2 = Timer("regex2(s)", "from __main__ import s, regex2; import re")
print "Regex2:", t_regex2.timeit()
t_regex3 = Timer("regex3(s)", "from __main__ import s, regex3; import re")
print "Regex3:", t_regex3.timeit()
結果:
Simple: 4.80558681488
jsbueno: 5.92122507095
Regex1: 3.21153497696
Regex2: 2.80767202377
Regex3: 6.64155721664
因此regex2贏得了時間。
這不是最漂亮的方法,但是:
re.match(r'([^A-Z]*[A-Z]){3}[^A-Z]*([A-Z].*)', line).group(2)
import re
strings = [
'adsgasdlkgasYasdgjaUUalsdkjgaZsdalkjgalsdkjTlaksdjfgasdkgj',
'oiwuewHsajlkjfasNasldjgalskjgasdIasdllksjdgaPlsdakjfsldgjQ',
]
for s in strings:
m = re.match('[a-z]*[A-Z][a-z]*[A-Z][a-z]*[A-Z][a-z]*([A-Z].+)', s)
if m:
print m.group(1)
解析幾乎總是涉及正則表達式。 但是,正則表達式本身並不構成解析器。 從最簡單的意義上講,解析器包括:
text input stream -> tokenizer
通常它還有一個額外的步驟:
text input stream -> tokenizer -> parser
標記生成器處理打開輸入流並以適當的方式收集文本,以便程序員不必考慮它。 它消耗文本元素,直到只有一個匹配可用。 然后它運行與此“令牌”關聯的代碼。 如果你沒有tokenizer,你必須自己滾動它(在偽代碼中):
while stuffInStream:
currChars + getNextCharFromString
if regex('firstCase'):
do stuff
elif regex('other stuff'):
do more stuff
這個循環代碼充滿了陷阱,除非你一直構建它們。 計算機也可以很容易地從一組規則中生成它。 這就是Lex / flex的工作方式。 您可以讓與令牌關聯的規則將令牌傳遞給yacc / bison作為解析器,從而添加結構。
請注意,詞法分析器只是一個狀態機 。 當它從一個州遷移到另一個州時,它可以做任何事情 。 我寫了一些詞法分析器,用於從輸入流中刪除字符,打開文件,打印文本,發送電子郵件等等。
所以,如果您只想在第四個大寫字母后收集文本,那么正則表達式不僅合適,而且是正確的解決方案。 但是如果你想要解析文本輸入 ,使用不同的規則來做什么和未知的輸入量,那么你需要一個詞法分析器/解析器。 我建議PLY,因為你使用python。
caps = set("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
temp = ''
for char in inputStr:
if char in caps:
temp += char
if len(temp) == 4:
print temp[-1] # this is the answer that you are looking for
break
或者,您可以使用re.sub
來刪除任何不是大寫字母的內容並獲取剩下的第四個字符
另一個版本......不是那么漂亮,但完成工作。
def stringafter4thupper(s):
i,r = 0,''
for c in s:
if c.isupper() and i < 4:
i+=1
if i==4:
r+=c
return r
例子:
stringafter4thupper('adsgasdlkgasYasdgjaUUalsdkjgaZsdalkjgalsdkjTlaksdjfgasdkgj')
stringafter4thupper('oiwuewHsajlkjfasNasldjgalskjgasdIasdllksjdgaPlsdakjfsldgjQ')
stringafter4thupper('')
stringafter4thupper('abcdef')
stringafter4thupper('ABCDEFGH')
分別結果:
'ZsdalkjgalsdkjTlaksdjfgasdkgj'
'PlsdakjfsldgjQ'
''
''
'DEFGH'
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.