使用词典,如何在列表中找到/跟踪重复的GUID?

时间:2011-12-10 08:37:20

标签: python dictionary guid

我使用WiX XML文件工作很多,而且WiX中的每个对象都需要一个GUID。为了避免复制粘贴错误,我设置了一种方法来排序和显示所有重复的GUID,给定一个这样的列表(使用findegrep创建):

./A2.Spam.EggsMgrSvc/__A2.Spam.EggsMgrSvc.wixproj:3A206536-FBCC-4911-AF2B-CBCD76E2C23E
./A2.Spam.TrojanBunnies/Files/Files.wxs:1F372E8A-95B9-49AC-84A6-998E7F5B0689
./A2.Spam.TrojanBunnies/Files/Files.wxs:4BB4FBAD-032A-4FBA-8B81-8AA2876E6765
./A2.Spam.TrojanBunnies/Files/File1.wxs:E289D834-4421-4DCE-B0A8-94C09978058A
./A2.Spam.TrojanBunnies/Files/Files.wxs:083863F1-70DE-11D0-BD40-00A0C911CE86
./A2.Spam.TrojanBunnies/Files/File1.wxs:E289D834-4421-4DCE-B0A8-94C09978058A
./A2.Spam.TrojanBunnies/Files/Files.wxs:083863F1-70DE-11D0-BD40-00A0C911CE86
./A2.Spam.TrojanBunnies/Files/File2.wxs:E289D834-4421-4DCE-B0A8-94C09978058A

采用以下格式:

  3 E289D834-4421-4DCE-B0A8-94C09978058A
       2 ./A2.Spam.TrojanBunnies/Files/File1.wxs
       1 ./A2.Spam.TrojanBunnies/Files/File2.wxs
  2 083863F1-70DE-11D0-BD40-00A0C911CE86
       2 ./A2.Spam.TrojanBunnies/Files/Files.wxs

在GUID旁边计算GUID的出现次数,然后在每个文件中计算该GUID的出现次数。

我想出了以下脚本(产生了上面的输出)。我还是Python新手,我真的想了解字典及其实际用途。使用嵌套词典是正确的方法吗?我选择了字典,因为我认为这是添加/跟踪唯一条目的最简单方法。虽然,使用像parent_dict['child_dict_key']['value_key']这样的语法感觉有点奇怪,但也许我可以使用items()或其他可迭代的方法/技术:

#!/usr/bin/env python

guids = {}
f_and_g = open( 'files-and-guids.txt', 'r')

for fg in f_and_g.readlines():
    fname, guid = map( str.strip, fg.split(':') )

    if guid not in guids:
        guids[guid] = { 'count': 1, 'files': {} }
    else:
        guids[guid]['count'] += 1

    ## Count how many times a GUID was used in a given file
    if fname not in guids[guid]['files']:
        guids[guid]['files'][fname]  = 1
    else:
        guids[guid]['files'][fname] += 1

## Sort by total count for a given GUID
for guid in sorted( guids, key=lambda x:guids[x]['count'], reverse=True):
    ## Skip printing if count is below threshold
    if guids[guid]['count'] < 2:
        continue
    guid_dict = guids[guid]
    print '{:>3} {}'.format( guid_dict['count'], guid )
    ## Sort by filename counts
    for fname in sorted( guid_dict['files'],
                         key=lambda x: guid_dict['files'][x], reverse=True ):
        fname_cnt = guid_dict['files'][fname]
        print '{:>8} {}'.format( fname_cnt, fname)

3 个答案:

答案 0 :(得分:2)

我会这样做,虽然我实际上没有测试过这段代码:

#!/usr/bin/env python

import collections
import operator

guids = collections.defaultdict(collections.Counter)
f_and_g = open('files-and-guids.txt', 'r')

for fg in f_and_g:
    fname, guid = map(str.strip, fg.split(':'))

    guids[guid][fname] += 1

## Sort by total count for a given GUID

guids_counts_totals = [(guids, counts, sum(counts.itervalues()))
                       for guids, counts
                       in guids.iteritems()]

guids_counts_totals_sorted = sorted(guids_counts_totals,
                                    key=operator.itemgetter(2),
                                    reverse=True)

for guid, counts, total in guids_counts_totals_sorted:
    ## Skip printing if count is below threshold
    if total < 2:
        continue

    print '{:>3} {}'.format(total, guid)

    ## Sorting by filename counts
    fnames_counts_sorted = sorted(counts.iteritems(),
                                  key=operator.itemgetter(1), reverse=True)
    for fname, count in fnames_counts_sorted:
        print '{:>8} {}'.format(count, fname)

这里有一些变化:

  • 使用collections.defaultdictcollections.Counter代替重复检查是否存在密钥,如果密钥不存在则将其设置为1
  • 不通过存储每个GUID和每个文件名的计数来复制数据。您只需总结GUID的每个文件名的所有计数
  • 即可
  • dict.itervalues()进行排序和迭代,而不是仅仅使用键,然后查找其值
  • 使用operator.itemgetter()代替lambda表达式
  • 根据PEP 8
  • 进行间距

答案 1 :(得分:2)

另一种变化:

#!/usr/bin/env python
import fileinput
from collections import defaultdict, Counter

# count guids
perfile = defaultdict(Counter)
total = Counter()
for line in fileinput.input():
    fname, guid = map(str.strip, line.split(':'))
    perfile[guid][fname] += 1
    total[guid] += 1

# print most common guid first
for guid, count in total.most_common():
    if count < 2: continue # skip printing if count is below threshold
    print '{:>3} {}'.format(count, guid)
    # sorting by filename counts
    for fname, fname_cnt in perfile[guid].most_common():
        print '{:>8} {}'.format(fname_cnt, fname)

实施例

$ python2.7 count-guid.py  input 
  3 E289D834-4421-4DCE-B0A8-94C09978058A
       2 ./A2.Spam.TrojanBunnies/Files/File1.wxs
       1 ./A2.Spam.TrojanBunnies/Files/File2.wxs
  2 083863F1-70DE-11D0-BD40-00A0C911CE86
       2 ./A2.Spam.TrojanBunnies/Files/Files.wxs

如果脚本清晰并且适用于你,请不要过度思考。

答案 2 :(得分:0)

基于我再次尝试的一些答案,为了让我的生活更加困难而避开任何其他的lib:

def MyCounter(l):
    d = dict()
    for i in l:
        if i not in d:
            d[i] = 1
        else:
            d[i] += 1
    return d

def main():
    guids = dict()
    f_and_g = open('files-and-guids.txt', 'r')
    for fg in f_and_g.readlines():
        fname, guid = map(str.strip, fg.split(':'))
        if guid not in guids:
            guids[guid] = [fname]
        else:
            guids[guid] += [fname]

    ## Sort by total count for a given GUID
    for guid in sorted(guids, key=lambda guid: len(guids[guid]), reverse=True):
        ## Skip printing if count is below threshold
        if len(guids[guid]) < 2: continue
        guid_list = guids[guid]
        print '{:>3} {}'.format( len(guid_list), guid )
        ## Sort by filename counts
        counts = MyCounter(guid_list)
        for fname, fname_cnt in sorted(counts.iteritems(), key=lambda x:x[1],
                                   reverse=True):
            print '{:>8} {}'.format(fname_cnt, fname)