繁体   English   中英

在Python中解析第4行大写字母?

[英]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]
  • 返回第4个大写字母的位置: .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并没有发布; 看到j​​sbueno的方法是相似的; 除了对每个字符(甚至是小写字母)进行加法/计数比较之外,他的方法稍慢。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM