2015-05-21 57 views
0

我有以下脚本,它循环访问css规则的文本文件,并将每个规则及其属性存储在字典中(代码欢迎的改进,我只有刚开始使用Python):Python - 在字典中查找重复对/值

findGroups.py

import sys 
source = sys.argv[1] 
temp = open('pythonTestFile.txt', 'w+') 
di = {} 
with open(source, 'r') as infile: 
    for line in infile: 
     # if line starts with . or #, contains _ or - between 
     # words and has a space and opening brace(ie is css rule name) 
     if re.search('((([\.\-\'])?\w+\s?\{', line): 
      key = line.replace("{", "") 
      di[key] = [] 
      line = next(file) 
      while "}" not in line: 
       # remove trailing whitespace and \n 
       line = ' '.join(line.split()) 
       di[key].append(line) 
       line = next(infile) 
temp.close(); 

的Source.txt

* { 
    min-height: 1000px; 
    overflow: hidden; 
} 

.leftContainerDiv { 
    font-family: Helvetica; 
    font-size: 10px; 
    background: white; 
} 

#cs_ht_panel{ 
    font-family:10px; 
    display:block; 
    font-family:Helvetica; 
    width:auto; 
} 
//...etc 

最好,我想输出看起来像这样(为便于阅读,建议也欢迎):

pythonTestFile.txt

Group 1, count(2) - font-family: Helvetica; + font-size: 10px; 
Group 2: //...etc 

我想现在要做的就是找出哪些CSS属性是重复出现的组,例如,如果font-size:10px和font-family:Helvetica在规则中一起出现,则此组是否出现在其他任何规则中,并且出现了多少次。

我不完全知道在哪里与此去,我无法弄清楚如何甚至开始某种比较算法,或者如果字典是正确的数据结构来存储文本。

编辑:在回应评论时,我无法使用第三方库。这个脚本将在Red Hat VM上使用,并且只有预先认可的软件才能被推送到这些上,我无法下载库或软件包

+1

我不介意downvote,但至少解释为什么这个问题downvoted,以便我可以修复它 – jbailie1991

+1

你的努力确实是写一个CSS解析器。是不是像tinycss(https://pythonhosted.org/tinycss/)是一个很好的起点,并修改或使用它的API? – gabhijit

+1

我将在此问题中添加更多详细信息,但不能使用第三方库。这适用于红帽虚拟机,并且我们可以在这些虚拟机上使用哪些软件有严重的限制。基本上我们不能下载任何第三方软件,我们使用的任何东西都被各种人预批准并推送到虚拟机上,所以我只能真正访问任何已经安装在红帽实例上的编程语言或工具。我同意,也许手滚动/修改/使用现有的CSS解析器可以使这个更简单,但不幸的是它不是一个选项 – jbailie1991

回答

1

为每个CSS属性指定一个不同的素数,如:

{ 
    'diplay: block': 2 
    'font-size: 10px': 3, 
    'font-family: Helvetica': 5, 
    'min-height: 1000px': 7, 
    'overflow: hidden': 11, 
    'width: auto': 13, 
    'background: white': 17, 
} 

然后做一个字典,其中键是CSS选择器(你叫什么“规则”)和值是所有属性的产品有:

{ 
    '#cs_ht_panel': 390, # 2 * 3 * 5 * 13 
    '*': 77, # 7 * 11 
    '.leftContainerDiv': 255, # 3 * 5 * 17 
} 

现在你可以很容易地确定两件事:

  • 通过查看选择器号码是否可被该号码整除,选择器(“规则”)具有属性x(由其素数表示)或一组属性{x,y,z,..}(由它们的素数乘积表示)。
    例如什么选择器都有'font-family: Helvetica'(5)和font-size: 10px(3)?所有且仅可被15整除的那些。
  • 通过计算GCD(最大公约数),两个选择器具有的所有属性。
    例如GCD(390,77)= 1 - >它们的共同点
    GCD(390,255)=没有属性15 - >分比化 - > 3 * 5

您还可以找到通过迭代最常见的基团在所有选择器值中,查找所有不是素数的适当除数,并保留一个字典,以节省找到该除数的数量。每个除数是一个组,你可以通过分解它来找到它的元素。

  • 390 - > 6 10 15 26 30 39 65 78 130 195
  • 255 - > 15 51 85
  • 77 - >

所以,你有两次15,并且所有的别人1次。这意味着组15中有2次出现,这是属性3和5的组。

最后一个计算步骤是2^n,其中n是该CSS选择器中的属性数。这不应该是一个问题,因为大多数选择器的属性不到10个,但是有超过20个属性,所以你开始陷入困境。我建议通过删除前缀(moz-,webkit-)和后缀(-left,-right,-top,-bottom)来压缩属性

对于真正的CSS文件, )做所有这些只是使用集合和他们的操作(交叉等)而不是数字,产品和素数;但是这不是更酷? ;)

+0

在重新阅读你的答案时,在给出所有css属性的部分之后一个素数,选择器被赋值为这些值的产物,你将如何搜索这个?你的意思是说用户会为他们想要看到的群组输入质数,或者是否应该自动化,以便脚本能够通过选择器并确定哪些组存在? – jbailie1991

+0

这是我答案的最后一部分,首先是:“你也可以找到最常见的群体......” – LeartS

+0

啊是的,我明白了,我想我明白你的解决方案了。 – jbailie1991

1

基于上面解释的想法的解决方案 - 而不是使用素数 - 我使用集合和有序列表。可能这是你想要的吗?

import re 
import itertools 

f = open('css_test.txt', 'r') 
lines = f.readlines() 
lines_str = ' '.join([l.strip() for l in lines]) 
#print lines_str 

r = re.compile(r'.*?{(.*?)}') # Get All strings between {} 
groups = r.findall(lines_str) 
#print groups 

# remove any stray spaces in the string and create groups of attributes like 
# style: value 
grps = [] 
for grp in groups: 
    grps.append(filter(lambda x: len(x) > 0, grp.strip().split(';'))) 


# clean up those style: value attributes so that we get 'style:value' 
# without any spaces and also collect all such attributes (we'd later create 
# a set of these attributes) 
grps2 = [] 
all_keys = [] 
for grp in grps: 
    grp2 = [] 
    for g in grp: 
     x = ':'.join([x.strip() for x in g.split(':')]) 
     grp2.append(x) 
     all_keys.append(x) 
    grps2.append(grp2) 
set_keys = set(sorted(all_keys)) 

print set_keys 
print '***********' 
set_dict = {} 
# For each combination of 2 of keys in the set find intersection of this 
# set with the set of keys in the cleaned up groups above 
# if intersection is the set of 2 keys: initialize a dictionary or add 1 
for x in itertools.combinations(set_keys, 2): 
    for g in grps2: 
     set_x = set(x) 
     set_g = set(g) 
     #print "set_g : ", set_g 
     if set_x & set_g == set_x: 
      print set_x 
      if set_dict.has_key(x): 
       set_dict[x] += 1 
      else: 
       set_dict[x] = 1 

# print everything 
print set_dict 

即使这个解决方案是不完全匹配你想要的 - 可能是你可以遵循的认为在你正在尝试做的到上面的行?

+0

我在代码中遇到了一些麻烦(不是你的错,我是Python的新手和使用套件)。你是否基本上为包含其属性的每个选择器创建一个集合,并使用每个其他集合的交集操作来查找公共对? – jbailie1991

+0

简答 - 是的。长答案 - 约定的代码不太可读。很多代码除了清理字符串外什么都不做,因此我们可以“独立化”它们。但让我解释一下基本的想法。使用正则表达式来找到我们感兴趣的'组'。现在我们处理'每个组',对于每个组我们获得'A-V'对。 _all_这样的A-V(作为字符串)的集合将是所有A-V的全局集合。然后,我们从这个全局集合中创建2个a-vs的所有'组合',并且以组的形式搜索它们(intersection == 2A-vs),最后在出现的字典中收集它们。 HTH – gabhijit