【问题标题】:Looking for an optimized Regex/other solution for mass string matching寻找用于大规模字符串匹配的优化正则表达式/其他解决方案
【发布时间】:2020-02-08 01:51:19
【问题描述】:

我当前代码的一部分涉及(伪)随机生成相当多对不同长度的字符串 - 具体来说,

shiftspace = {
    2: 20,
    3: 79,
    4: 250,
    5: 791,
    6: 2500,
    7: 7906,
    8: 25000,
    9: 79059
}

其中 key = 字符串的长度和 value = 要生成的对。这些字符串可以由字符 0-9 的任何变体以及代表任何字符的字符 * 组成。问题在于我不希望这些集合包含任何重复。在我实现 * 之前,我只是检查了是否已经生成了一个新生成的数字,但是现在字符串可以“重叠”,尽管它们是不相同的。例如,“*056*3”的存在使生成的序列“*05693”或“905623”或“4056*3”无效

目前解决方案是我的老“朋友”正则表达式。我只是生成一个由 [char1*][char2*][char3*] 等组成的正则表达式字符串,它能够完美地挑选出这些匹配项。问题是速度——我检查每个条目的正则表达式,当达到更高的数字时,它会及时膨胀(要检查的最大可能集可以达到近 80k 条目)。过去需要 5 分钟 * 现在需要一个小时或更长时间。完成这一切的代码如下。

def regexfit(strang,set):
    regex = ""
    for char in strang:
        regex = regex + "["+char+"*]"
    match = re.search(regex,set)
    if match:
        return True

def shiftgen():
    baseset = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "*"]
    shiftset = {}
    keys = list(shiftspace.keys())
    togo = sum(shiftspace.values())
    print(togo)
    for i in range(min(keys), max(keys) + 1):
        print(i)
        shiftset[i] = {}
        shiftset[i]["0"] = 0
        for j in range(shiftspace[i]):
            num = 0
            num2 = 0
            while num == num2:
                num = np.random.choice(baseset, size=i)
                num = "".join(num)
                numset = [i for i in str(num)]
                while any((regexfit(num,x) for x in shiftset[i].keys())):
                    num = np.random.choice(baseset, size=i)
                    num = "".join(num)
                    numset = [i for i in str(num)]
                if len(str(num)) < i:
                    for k in range(i - len(str(num))):
                        numset.append(0)
                num2 = np.random.choice(numset, size=i, replace=False)
                num2 = "".join(num2)
            shiftset[i][num] = num2
            togo = togo - 1
            if togo % 1000 == 0:
                print([num, num2])
        shiftset[i].pop("0")
    return shiftset

我考虑过的事情:

  • 连接所有序列以检查成一个巨大的字符串(使用分隔符 char 以防止正则表达式在两个序列之间匹配)并在其上使用正则表达式。我对 re 引擎的内部结构了解得不够多,不知道它最终会有多好。一旦我对上面代码的第一次定时测试完成后,我将测试它的运行速度。

  • 将这些带星号的字符串作为它们可以采用的所有可能排列添加到数据库中(即:将 2* 添加为 21、22、23 等)>。然后您只需搜索数据库(或者,如果您生成带星号的字符串,则搜索每个排列并检查是否有正数)。我非常怀疑这会更有效,但你永远不知道。

  • 某种解决方案,您可以在其中生成序列,确定它们之前没有生成过或者会被之前生成的序列“覆盖”,从而完全消除了搜索的需要。不是真正的解决方案,因为我不知道该怎么做,只是把它扔在那里。

  • 某种更高级的正则表达式,计算速度更快。

  • 某种神奇的模块/公式由于某种原因非常擅长做这件事。

  • 黑魔法、祭祀上古之神等

TL:DR 我正在寻找一种更快的方法来匹配具有许多潜在排列的序列,以确保以前没有选择过排列。

【问题讨论】:

    标签: python regex python-3.x


    【解决方案1】:

    几个cmets,太长了,无法评论...

    您在当前代码中构建的正则表达式实际上并不正确。当您在strang 中遇到* 时创建的字符类[**] 只会匹配set 中的*,您实际上想匹配任何 字符。当您在strang 中遇到* 时,您实际上应该在正则表达式中输入.。例如:

    regex = ''.join('.' if c == '*' else '[*' + c + ']' for c in strang)
    

    其次,您应该使用re.match 而不是re.search,因为它将搜索锚定到字符串的开头,因此可以防止从输入中的每个位置开始搜索。这将稍微提高速度。

    最后,它应该比使用正则表达式(我的测试表明 3-4x)来简单地迭代每个值中的字符更快,一一比较它们并在没有匹配时立即失败,例如

    def regexfit(strang,set):
        for a, b in zip(strang, set):
            if a != '*' and b != '*' and a != b:
                return False
        return True
    

    【讨论】:

    • 感谢您的全面回答。我没有考虑过与正则表达式的交互。我会用你的 zip 函数切换我的函数,看看它最终会快多少。
    • @Arelus 不用担心。您应该暂时保留问题(不接受答案),因为其他用户可能有更好的建议......
    • 我已经这样做了,谢谢。我已经对其进行了测试,它并没有快 3 到 4 倍,可能是 2 倍左右(可能只是我的蹩脚笔记本电脑),但它是一个很好的改进,并且实际上也像你指出的那样工作。
    • @Arelus 不用担心 - 抱歉您没有看到同样的改进...您可能还想考虑在 codereview.stackexchange.com 上发布这个问题
    猜你喜欢
    • 2017-08-25
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    • 2019-10-29
    • 1970-01-01
    • 2019-03-16
    • 2017-01-15
    相关资源
    最近更新 更多