2017-02-09 90 views
2

我正在编写的Python应用程序需要从源代码中提取标识符和文本字符串。它发现的一小部分是(看似)随机字符串。我想过滤它们,但到目前为止还没有能够创建一个正则表达式来做到这一点。不可能仅按长度进行过滤,因为有一些非常长的标识符是有效的。下面是一个例子采取随机的,与相同长度的有效的标识符:如何匹配实际上是随机的字符串?

UGxhemEgZGUgaWZXNaWdhZGyOiBDSUWRVNUQVYtSVBOIFVuaWQ 
NSApplicationDidChangeScreenParametersNotification 

是否有写一个正则表达式或其它检测系统,该系统将检测像这样的垃圾序列的方法吗?我开始怀疑,如果不测试字符串对大量字词典的测试是不行的,我认为这些字典很容易出错并且计算密集。然而,也许有人更聪明的知道检测或匹配像这样的随机序列的方法?

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

编辑#1 2017-02-08:进一步思考,我想到一个可能的方法可能是一个正则表达式匹配一行中最小数量的唯一字符。例如,第二个字符必须与第一个不同,第三个不同于前两个,第四个不同于第三个,等等。足够长的版本可能会捕获很多随机序列。然而,看着不同的正则表达式运算符,我看不到一个版本(缺少更好的单词)的“负面背景参考”或“匹配其他比你刚刚匹配的东西”。如果有人知道这个变化,也许我可以让它工作。

编辑#1 2017年2月10日:我很担心,我写我的两个例子字符串的方式上面可能被误解为一个字符串。上面的例子是两个不同长度的字符串,如果不清楚的话,我诚挚的道歉。这里有更多的例子。每一行都是一个单独的标识符。这也显示了不同的长度。

shouldBeAbleToCountLiveNeighboursOfACellOnDiagonalsAndStraightLines 
eXNZWzIGbHRpbWVkaWEgYWkIGFuaWhdGlvbiBkaXNcmlidXRlZCNCpUgRGlzdHJpYnV 
dWxLXRvbGVyYWIHJlYWwtdGltZSBzeXNZWzLgKlSBEaXNcmlidXRlZCBBcmNoaXRlYR 
dGhIExvIHNYmltbMgYSBsYSBwWdpbmEgeSBsbyBhbnVuYlhbWzIGVuIGVsIHByhpbWg 
aGUgYuZmVyZWjZSBwcmjZWVkaWncygDQoNClNYmpcNpbNCkluIGyZGVyIHRvIHN 
YQKUGFyYTogZXNYFyQGluYWlcCteAKQMIExaXMgQSgUGluZWRhDQpDQzogQuYVw 
thehasSizeMatcherShouldMatchACollectionWithExpectedSize 
QycmVvIGRlIERpcVtaWhYnDsgZGUgYWNaXZpZGFkZXMgZGUgbGEg 
NSAppleEventManagerWillProcessFirstEventNotification 
SNMTransformGizmoRotationControllerPerformTransform 
RndkOiBEaWZcnDsgZGUgYudmjYXRvcmlhIFNVTUJVCBlbiBSRUJ 

不管它的价值,我把从大约900 GitHub的仓库半随机选择通过我的应用程序拉引擎收录a list of the 1000 longest identifiers。它包含真实标识符和随机字符串。

+0

NLTK可能证明对此有用。 – sytech

+1

假设有效令牌包含英文,无效令牌将有更多的连续4个或更多的辅音。 – swbandit

+0

乍看之下,如果字符串的长度足够大(25-80也可以),那么怎么计算每个字母的频率并将这个分布与典型的英语语言进行比较。 –

回答

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 

起初,我试图寻找单词的字符串。这有不好的结果,因为它检测到的词的单词(例如:通知还包含单词“不”,“如果”,“猫”,“在”等) 所以我决定分割的首都,并检查各元素反对字典。这也不能很好地工作,因为许多单字母单词被证明是英语单词:

U - 形容词|英式|非正式的:(语言或社交行为的)特征或适用于鞋帮社会等级。 ( “U礼仪”)

谁知道?

所以最后,我决定忽略任何短的话,我不知道(相当多的事实证明),并限制对普通短词。 我可以进一步使用NLTK或类似的工具以类似于乱码检测器的方式检查单词对频率。但它已经做了很多次了,我宁可不要。

+0

感谢您的支持。在上面的示例中,我不确定我的问题的措辞是否清楚地表明,我给出的示例包含2个单独的字符串,而不是一个跨行的字符串。如果不清楚,我真的很抱歉。我已经为这个问题添加了一些澄清的编辑,并且还提供了一个指向包含更多示例的pastebin的指针。 – mhucka

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 

对我来说结果很有趣。

UPDATE:

我已经试过机器学习。我用了一个有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)

Finaly,神经元可用于:

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 

我真的很感动有多么容易被意识到它会产生多好的结果。

+0

@ roman-fursenko真的很酷:-)。我用一个指针指向从随机选择的github存储库中取出的1000个真正的字符串的pastebin,更新了问题。我计划尽快尝试你的代码。感谢您的详细努力。 – mhucka