[英]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.