如何匹配实际上随机的字符串?

时间:2017-02-09 05:11:07

标签: python regex random pattern-matching

我正在编写的Python应用程序需要从源代码中提取标识符和文本字符串。它发现的一小部分是(看似)随机字符串。我想过滤掉它们,但到目前为止还没有能够创建一个正则表达式。不能仅按长度过滤,因为有一些非常长的标识符是有效的。以下是随机拍摄的示例,与相同长度的有效标识符进行比较:

UGxhemEgZGUgaWZXNaWdhZGyOiBDSUWRVNUQVYtSVBOIFVuaWQ
NSApplicationDidChangeScreenParametersNotification

有没有办法编写一个regexp或其他检测系统来检测像这样的垃圾序列?我开始怀疑在没有针对大词典的测试字符串的情况下无法完成,我认为这些词典容易出错并且是计算密集型的。但是,也许有人会更聪明地知道检测或匹配这样的随机序列的方法吗?

这个问题的一个理想解决方案是一个可以将字符串作为输入的函数,并报告它是否是"可能"随机。它可能会产生漏报(误报一些随机字符串,因为它不是随机的),最好是概率很低,但它不能报告误报(当事件不是时,它就是随机的)。如果重要,字符串的长度范围应为25到80个字符。

编辑#1 2017-02-08 :进一步思考,我想到一种可能的方法可能是匹配一行中最小数量的唯一字符的正则表达式。例如,第二个字符必须与第一个字符不同,第三个字符与前两个字符不同,第四个字符与前一个字符不同,等等。足够长的版本可能会捕获大量随机序列。但是,看看不同的正则表达式运算符,我没有看到(缺少更好的单词)和#34;负反向引用的版本"或者"匹配其他的东西而不是你刚刚匹配的东西"。如果有人知道这方面的变化,也许我可以让它发挥作用。

编辑#1 2017-02-10 :我担心上面编写上面两个示例字符串的方式可能被误解为单个字符串。上面的例子是两个相同长度的单独字符串 - 如果不清楚,我真诚的道歉。这里有一些例子;每一行都是一个单独的标识符。这也有目的地显示出不同的长度。

shouldBeAbleToCountLiveNeighboursOfACellOnDiagonalsAndStraightLines
eXNZWzIGbHRpbWVkaWEgYWkIGFuaWhdGlvbiBkaXNcmlidXRlZCNCpUgRGlzdHJpYnV
dWxLXRvbGVyYWIHJlYWwtdGltZSBzeXNZWzLgKlSBEaXNcmlidXRlZCBBcmNoaXRlYR
dGhIExvIHNYmltbMgYSBsYSBwWdpbmEgeSBsbyBhbnVuYlhbWzIGVuIGVsIHByhpbWg
aGUgYuZmVyZWjZSBwcmjZWVkaWncygDQoNClNYmpcNpbNCkluIGyZGVyIHRvIHN
YQKUGFyYTogZXNYFyQGluYWlcCteAKQMIExaXMgQSgUGluZWRhDQpDQzogQuYVw
thehasSizeMatcherShouldMatchACollectionWithExpectedSize
QycmVvIGRlIERpcVtaWhYnDsgZGUgYWNaXZpZGFkZXMgZGUgbGEg
NSAppleEventManagerWillProcessFirstEventNotification
SNMTransformGizmoRotationControllerPerformTransform
RndkOiBEaWZcnDsgZGUgYudmjYXRvcmlhIFNVTUJVCBlbiBSRUJ

无论它值多少,我都会从我的应用程序中提取的pastebin a list of the 1000 longest identifiers来自大约900个GitHub存储库的半随机选择。它包含真实标识符和随机字符串。

2 个答案:

答案 0 :(得分:2)

首先,谢谢你,你的问题对我感兴趣,而且我正在寻找一些有趣的培训。下面我已经在你的帖子的评论中提到了我的想法,以及@swbandit的想法。此外,可以通过修改is_rnd函数在代码中添加任何其他策略。 我从这里找到的短词典中生成了有意识的字符串(https://gist.github.com/jbergantine/2390284)(当然这个字典很小,可能不具代表性,但我用它来测试)。这些字符串在代码中被称为strok。之后,生成相同长度的随机字符串(strrnd)。我只使用小写字符并假设字符串中没有空格。

如果字符串是随机的,则函数is_rnd1is_rnd2会返回True。函数is_rnd1检查最频​​繁的英文字符的频率' e' (12.7%)和最罕见的' z' (0.074%)。然而,在该功能中,频率的边界显着扩展。函数is_rnd2只是检查@swbandit建议的四个连续辅音的外观。

在代码的测试部分中,针对不同长度的字符串测试上述功能,其根据构成strok的字的数量来测量。函数is_rnd被调用两次。首先是strok,第二个是随机字符串。将随机定义或不定义字符串的错误相加。

所以这就是代码:

nouns = ['here is a list from gist.github.com/jbergantine/2390284']
allch = "abcdefghijklmnopqrstuvwxyz"

import numpy as np
import matplotlib.pyplot as plt
import random, string
import collections
import re

alb = 'etaoinshrdlcumwfgypbvkjxqz'

def frqlist(s):
    dic = collections.Counter(s)
    arr = np.zeros(len(alb))
    for key in dic:
        idx = alb.index(key)
        arr[idx] = float(dic[key])/float(len(s))
    return arr

def generate_strs(nw=1):
    strok = ''.join([nouns[random.randrange(0, len(nouns))] 
                     for i in range(nw)])
    rand_str = lambda n: ''.join([random.choice(string.lowercase) 
                         for i in xrange(n)])
    strrnd = rand_str(len(strok))
    return strok, strrnd

def is_rnd1(s):
    fq = frqlist(s)
    return not (fq[0] > 0.07 and fq[-1] < 0.01)

def is_rnd2(s):
    return re.search(r'[^aeiou]{4}', s)

maxwords = 12
nprobe = 1000

is_rnd = is_rnd1
nwa = []
err = []
print "Words\t% of errors"
for nw in range(1, maxwords):
    errok = 0
    errrnd = 0
    for i in range(0, nprobe):
        strok, strrnd = generate_strs(nw)
        if is_rnd(strok):
            errok += 1./nprobe
        if not is_rnd(strrnd):
            errrnd += 1./nprobe
    print nw, "\t", (errok*100. + errrnd*100.)/2.
    nwa.append(nw)
    err.append((errok*100. + errrnd*100.)/2.)

plt.plot(nwa, err)
plt.show()

以下是一些结果

For function is_rnd1
Words   % of errors
1   28.2
2   20.45
3   17.15
4   13.15
5   13.7
6   10.65
7   9.25
8   7.35
9   6.5

For function is_rnd2 (4 consecutive consonants)
Words   % of errors
1   23.15
2   13.0
3   13.55
4   17.75
5   22.2
6   24.35
7   27.7
8   30.6
9   33.25

For function is_rnd2 (6 consecutive consonants)
Words   % of errors
1   39.45
2   20.8
3   11.9
4   6.5
5   4.05
6   3.05
7   2.5
8   1.6
9   2.0

对我来说,结果很有趣。

<强>更新

我尝试过机器学习。我使用了一个有26个入口和一个出口的神经元。在入口处,提供字符串中字符的频率。如果字符串是随机的,则输出为1,否则为0。神经元由以下类描述:

class Neuron(object):
    def __init__(self, nin, wt=0.):
        self.nin = nin
        self.w = np.full(nin, wt, dtype=np.float32)
        self.out = 0.
        self.learnspd = 0.01

    def result(self, ins):
        self.out = np.sum(self.w * ins)
        self.out = 1. if self.out > 0.1 else 0.
        return self.out

    def correctw(self, ins, err):
        self.w = self.w + err*self.learnspd*ins

在定义神经元neuron = Neuron(len(alb))后,实现了它的学习过程:

def learning(neuron, nset, maxwords):
    for i in xrange(nset):
        nw = np.random.randint(1, maxwords+1)
        strok, strrnd = generate_strs(nw)
        fq = frqlist(strok)
        neurres = neuron.result(fq)
        errok = 0.0 - neurres
        neuron.correctw(fq, errok)
        fq = frqlist(strrnd)
        neurres = neuron.result(fq)
        errrnd = 1.0 - neurres
        neuron.correctw(fq, errrnd)

让我们学习learning(neuron, nset, maxwords)

最后,可以使用神经元:

def is_rnd_neuron(s, neuron):
    fq = frqlist(s)
    return bool(neuron.result(fq))

使用与上述相同的测试程序我已获得以下结果:

nset = 100
Words   % of errors
1   50.0
2   50.0
3   50.0
4   50.0
5   50.0
6   50.0
7   50.0
8   50.0
9   50.0
nset = 500
Words   % of errors
1   20.4
2   13.25
3   9.5
4   5.55
5   5.95
6   3.9
7   3.35
8   2.55
9   2.4
nset = 1000
Words   % of errors
1   16.95
2   9.35
3   4.55
4   2.4
5   1.7
6   0.65
7   0.4
8   0.15
9   0.1
nset = 5000
Words   % of errors
1   16.6
2   7.25
3   3.8
4   1.8
5   1.1
6   0.5
7   0.1
8   0.1
9   0.05

我真的很容易意识到它是如何容易实现的以及它产生的结果如何。

答案 1 :(得分:0)

你可以看一下依赖马尔可夫链的rrenaud's gibberish detector。你需要改变给定的代码以满足你的需要,因为你会寻找含有非乱码的乱码。但它可能有所帮助。

这可能是一个很好的起点。

但是... 我先尝试自己解决这个问题。这可能不是最好的答案,它完全依赖于以大写字母开头的每个单词(基于您给定的测试输入)。但是,我喜欢玩想法来获得一个效果很好的结果,但是对于更长的文本(你可能会看到的那种)来说会非常慢。

import enchant #Spellchecker
import re

endict = enchant.Dict("en_US")

#Test input...
instr = 'UGxhemEgZGUgaWZXNaWdhZGyOiBDSUWRVNUQVYtSVBOIFVuaWQNSApplicationDidChangeScreenP arametersNotification'
outstr = ''

acceptable_short_words = ['a', 'I', 'if', 'is'] #Any one or two letter words you can think of that are OK.

#Split the words based on capitals.
words = re.findall('[A-Z][^A-Z]*', instr)

def isWord(wordin):
    if(wordin in acceptable_short_words):
        return True
    elif(len(wordin) < 3):
        return False

    return endict.check(wordin)

for w in words:
    if(isWord(w)):
        outstr += " " + w

print(outstr.strip())

运行此脚本会导致:

I Application Did Change Screen Parameters Notification

起初我尝试在字符串中搜索单词。由于它在单词中检测到单词,因此效果不佳(例如:通知中还包含单词&#39; Not&#39;,&#39; if&#39;,&#39; cat&#39;,&#39; at&#39;等) 所以我决定拆分大写字母,并根据字典检查每个元素。这也不能很好地工作,因为许多单字母单词被证明是英文单词:

  

U - 形容词|英国|非正式:(语言或社会行为)特征或适合上层社会阶层。 (&#34; U manners&#34;)

谁知道?

所以最后,我决定忽略我不知道的任何简短的单词(事实证明很多)并将其限制为常见的短语。 我可以更进一步,使用NLTK或类似的工具以类似于乱码检测器的方式检查字对频率。但是我做了很多次,而不是。