【问题标题】:How can I use Regex to find a string of characters in alphabetical order using Python?如何使用 Regex 使用 Python 按字母顺序查找字符串?
【发布时间】:2018-02-02 18:05:30
【问题描述】:

所以我有一个我正在处理的挑战——在一个字符串中找到最长的字母字符串。例如,“abcghiijkyxz”应该导致“ghiijk”(是的 i 加倍)。

我一直在用循环来解决这个问题 - 遍历整个字符串,然后为每个字符,使用 lower 和 ord 开始第二个循环。编写该循环不需要帮助。

但是,有人向我建议 Regex 非常适合这类事情。我的正则表达式很弱(我知道如何获取静态集,我的前瞻性知识扩展到知道它们存在)。我将如何编写一个正则表达式来向前看,并检查未来的字符是否按字母顺序排列?还是建议使用正则表达式对这类事情不实用?

编辑:普遍的共识似乎是 Regex 对于这类事情确实很糟糕。

【问题讨论】:

  • Regex 对于这种事情是不实用的。
  • Regex 是一个 terrible 的选择。正则表达式没有“这个字母在字母表中这个字母之后”的概念;尝试用正则表达式解决这个问题就像尝试用正则表达式做数学一样。
  • 你可以看看codegolf.stackexchange.com 的某个人是否疯狂到想出一个正则表达式的解决方案
  • 嗯,这似乎很好地验证了我最初的“等等,真的!?”反应。对于这类问题,如果答案是“不,这是一个糟糕的主意”,那么表明问题已结束/已解决的最佳方式是什么?
  • 很好的问题。我想您可以回答它(..!!)并将您当前的代码添加为“正确的方式”。或者完全删除问题(考虑到它可能对其他人有帮助)。

标签: python regex python-3.x


【解决方案1】:

只是为了说明为什么正则表达式对于这类事情实用,这里有一个正则表达式可以匹配您给定的abcghiijkyxz 示例中的ghiijk。请注意,它还将匹配abcyxz,因为从技术上讲,它们应该被视为按顺序排列的最长的字母字符串。不幸的是,您无法单独使用正则表达式来确定哪个是最长的,但这确实为您提供了所有可能性。请注意,此正则表达式适用于 PCRE,不适用于 python 的 re 模块!另外,请注意python's regex 库当前不支持(*ACCEPT)。虽然我没有测试过,pyre2 package(使用 Cython 的谷歌 re2 pyre2 的 python 包装器)声称它是supports the (*ACCEPT) control verb,所以这个可能目前可以使用 python。

See regex in use here

((?:a+(?(?!b)(*ACCEPT))|b+(?(?!c)(*ACCEPT))|c+(?(?!d)(*ACCEPT))|d+(?(?!e)(*ACCEPT))|e+(?(?!f)(*ACCEPT))|f+(?(?!g)(*ACCEPT))|g+(?(?!h)(*ACCEPT))|h+(?(?!i)(*ACCEPT))|i+(?(?!j)(*ACCEPT))|j+(?(?!k)(*ACCEPT))|k+(?(?!l)(*ACCEPT))|l+(?(?!m)(*ACCEPT))|m+(?(?!n)(*ACCEPT))|n+(?(?!o)(*ACCEPT))|o+(?(?!p)(*ACCEPT))|p+(?(?!q)(*ACCEPT))|q+(?(?!r)(*ACCEPT))|r+(?(?!s)(*ACCEPT))|s+(?(?!t)(*ACCEPT))|t+(?(?!u)(*ACCEPT))|u+(?(?!v)(*ACCEPT))|v+(?(?!w)(*ACCEPT))|w+(?(?!x)(*ACCEPT))|x+(?(?!y)(*ACCEPT))|y+(?(?!z)(*ACCEPT))|z+(?(?!$)(*ACCEPT)))+)

结果:

abc
ghiijk
y
x
z

单个选项的解释,即a+(?(?!b)(*ACCEPT))

  • a+ 匹配 a(字面意思)一次或多次。这会捕获多​​个相同字符按顺序排列的情况,例如aa
  • (?(?!b)(*ACCEPT)) If 子句评估条件。
    • (?!b) if 子句的条件。负前瞻确保后面的内容是不是 b。这是因为如果不是b,我们希望下面的控制动词生效。
    • (*ACCEPT) 如果满足上述条件,我们接受当前解决方案。此控制动词使正则表达式成功结束,跳过模式的其余部分。由于此令牌位于捕获组内,因此只有该捕获组在该特定位置成功结束,而父模式继续执行。

如果不满足条件会发生什么?嗯,这意味着(?!b) 评估为假。这意味着后面的字符实际上是b,因此我们允许匹配(在这种情况下是捕获)继续进行。请注意,整个模式都包含在 (?:)+ 中,这允许我们匹配连续的选项,直到遇到 (*ACCEPT) 控制动词或行尾。

整个正则表达式的唯一例外是z。因为它是英文字母表中的最后一个字符(我认为它是这个问题的目标),所以我们不在乎后面是什么,所以我们可以简单地输入z+(?(?!$)(*ACCEPT)),这将确保在z 之后没有任何匹配项。相反,如果您想匹配 za(圆形字母顺序匹配 - idk,如果这是正确的术语,但对我来说听起来不错),您可以使用 z+(?(?!a)(*ACCEPT)))+,如 here 所示。

【讨论】:

  • 我喜欢你的解决方案,写得很好!鉴于问题没有将 z 循环到 a (谢天谢地),它有点没有实际意义,但我想知道 - 你在最后提到了将 Z 循环到 A 的字符串,但它看起来不像将 ZAB 注册为一个字符串 - 它只会给 ZA。为什么正则表达式对此不利的一个可能的附加点? (例如,想象一下尝试做 a-za-za-z ......)
  • @Selkie 谢谢 :) 我做了一个快速的小编辑,使链接在最后更加明显。请参阅我的编辑,其中包含链接here(在最后)。它包括一个 zzaabcddde 完全匹配的示例。要仅匹配 a-z,您将使用我在上面发布的正则表达式,但要循环匹配 a-z,您将使用链接显示的 z+(?(?!a)(*ACCEPT)))+ 子模式。
  • 我不确定我是否应该对此表示赞成,以表明它是可能的,或者否决,因为它显示了如何去做。这太棒了!
  • @jpaugh 谢谢,我发布了这个,老实说,我和你在同一条船上。我喜欢它,但我不喜欢它
【解决方案2】:

如前所述,正则表达式并不是最好的工具。由于您对连续序列感兴趣,因此可以使用单个 for 循环来完成:

def LNDS(s):
    start = 0
    cur_len = 1
    max_len = 1
    for i in range(1,len(s)):
        if ord(s[i]) in (ord(s[i-1]), ord(s[i-1])+1):
            cur_len += 1
        else:
            if cur_len > max_len:
                max_len = cur_len
                start = i - cur_len
            cur_len = 1
    if cur_len > max_len:
        max_len = cur_len
        start = len(s) - cur_len
    return s[start:start+max_len]

>>> LNDS('abcghiijkyxz')
'ghiijk'

我们记录了我们看到的非递减字符的总数,当非递减序列结束时,我们将其与我们之前看到的最长非递减序列进行比较,如果出现以下情况,则更新我们的“迄今为止最好的”它更长。

【讨论】:

  • 考虑到提问者的测试字符串和期望的结果,我认为这并不能完全回答同样的问题:他们想要最长的字母顺序 连续 字符序列,允许重复。
  • 你能在 Ideone 上做一个演示吗?我测试过,它没有返回正确的结果。
  • 我的意思是,我很欣赏“已解决”的代码,但我希望自己完成它 - 良好的学习体验和所有这些 :)
  • @Imran:检查此ideone.com/pWfsIF。结果不一致。
  • 好的,这是最后检查的问题。我修好了。
【解决方案3】:

生成所有正则表达式子字符串,如 ^a+b+c+$(从最长到最短)。 然后将这些正则表达式中的每一个与“abcghiijkyxz”的所有子字符串(从最长到最短)进行匹配,并在第一次匹配时停止。

def all_substrings(s):
    n = len(s)
    for i in xrange(n, 0, -1):
        for j in xrange(n - i + 1):
            yield s[j:j + i]

def longest_alphabetical_substring(s):
    for t in all_substrings("abcdefghijklmnopqrstuvwxyz"):
        r = re.compile("^" + "".join(map(lambda x: x + "+", t)) + "$")
        for u in all_substrings(s):
            if r.match(u):
                return u

print longest_alphabetical_substring("abcghiijkyxz")

打印“ghiijk”。

【讨论】:

  • 这既可笑又奇美。
【解决方案4】:

正则表达式char+ 意思是a+b+c+...

详情:

  • + 匹配一次到无限次

Python 代码

import re

def LNDS(text):
    array = []

    for y in range(97, 122):  # a - z
        st = r"%s+" % chr(y)
        for x in range(y+1, 123):  # b - z
            st += r"%s+" % chr(x)
            match = re.findall(st, text)

            if match:
                array.append(max(match, key=len))
            else:
                break

        if array:
            array = [max(array, key=len)]

    return array

输出

print(LNDS('abababababab abc')) >>> ['abc']
print(LNDS('abcghiijkyxz')) >>> ['ghiijk']

对于字符串abcghiijkyxz 正则表达式:

a+b+                    i+j+k+l+
a+b+c+                  j+k+
a+b+c+d+                j+k+l+
b+c+                    k+l+
b+c+d+                  l+m+
c+d+                    m+n+
d+e+                    n+o+
e+f+                    o+p+
f+g+                    p+q+
g+h+                    q+r+
g+h+i+                  r+s+
g+h+i+j+                s+t+
g+h+i+j+k+              t+u+
g+h+i+j+k+l+            u+v+
h+i+                    v+w+
h+i+j+                  w+x+
h+i+j+k+                x+y+
h+i+j+k+l+              y+z+
i+j+
i+j+k+

Code demo

【讨论】:

  • @Rawing 我同意。我只是给出了如何使用正则表达式和一些编码来完成它的想法。我的帖子还没写完,所以...
  • 我可以看到你提议的大纲,我明白了,这是有道理的。问题是 - 这与简单的 ord(s)+1==ord(s+1) 有显着不同吗?如果是这样,怎么做?如果不是,那么使用正则表达式而不是 ord 有什么实际好处?
  • @Rawing 已更新。
【解决方案5】:

要真正“解决”问题,您可以使用

string = 'abcxyzghiijkl'

def sort_longest(string):
    stack = []; result = [];

    for idx, char in enumerate(string):
        c = ord(char)
        if idx == 0:
            # initialize our stack
            stack.append((char, c))
        elif idx == len(string) - 1:
            result.append(stack)
        elif c == stack[-1][1] or c == stack[-1][1] + 1:
            # compare it to the item before (a tuple)
            stack.append((char, c))
        else:
            # append the stack to the overall result
            # and reinitialize the stack
            result.append(stack)
            stack = []
            stack.append((char, c))

    return ["".join(item[0]
        for item in sublst) 
        for sublst in sorted(result, key=len, reverse=True)]

print(sort_longest(string))

产量

['ghiijk', 'abc', 'xyz']

在这个例子中。


这个想法是遍历字符串并跟踪stack 变量,该变量由您使用ord() 填充。

【讨论】:

    【解决方案6】:

    使用正则表达式真的很容易!

    (此处使用尾随上下文)

    rexp=re.compile(
        "".join(['(?:(?=.' + chr(ord(x)+1) + ')'+ x +')?'
                for x in "abcdefghijklmnopqrstuvwxyz"])
        +'[a-z]')
    
    a = 'bcabhhjabjjbckjkjabckkjdefghiklmn90'
    
    re.findall(rexp, a)
    
    #Answer: ['bc', 'ab', 'h', 'h', 'j', 'ab', 'j', 'j', 'bc', 'k', 'jk', 'j', 'abc', 'k', 'k', 'j', 'defghi', 'klmn']
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-08-29
      • 2023-03-12
      • 1970-01-01
      • 1970-01-01
      • 2014-11-26
      • 2014-08-05
      • 1970-01-01
      相关资源
      最近更新 更多