【问题标题】:python to pair items from two large listspython将两个大列表中的项目配对
【发布时间】:2016-06-20 21:07:13
【问题描述】:

我有两个文件:

  • file1(2 亿行)格式:email:hash1:hash2
  • file2(9000 万行)格式:hash:plaintext

我想要做的是将 file1 中的 hash(1 或 2) 替换为 file2 中的纯文本。我尝试使用我之前在此处two lists, faster comparison in python 提出的问题的解决方案(实际代码粘贴在下面),但不幸的是,对于如此大的数据集,它并没有那么快。它适用于较小的文件(少量行),但不适用于较大的文件。

您对“更快”处理这两个文件有何建议?

编辑:上面提到的源代码

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys, os

def banner():
    print('\n%s v 1.0\nby d2@tdhack.com\n' % sys.argv[0])

def getlength(fname):
    return sum(1 for line in open(fname))

def ifexist(fname):
    if not os.path.isfile(fname):
        banner()
        print('[-] %s must exist' % fname)
        sys.exit(1)

def replace(l, X, Y):
  for i,v in enumerate(l):
     if v == X:
        l.pop(i)
        l.insert(i, Y)

if len(sys.argv) < 2:
    banner()
    print('[-] please provide CRACKED and HASHES files')
    sys.exit(1)

CRACKED=sys.argv[1]
HASHES=sys.argv[2]

ifexist(CRACKED)
ifexist(HASHES)

banner()
print('[i] preparing lists from "%s" [%d lines] and "%s" [%d lines]' %(CRACKED, getlength(CRACKED), HASHES, getlength(HASHES)))
with open(CRACKED) as crackedfile:
    cracked = dict(map(str, line.split(':', 1)) for line in crackedfile if ':' in line)

hashdata = [line.rstrip('\n') for line in open(HASHES)]

print('[i] pairing items, this will take a while so please be patient')
for item in hashdata:
    if item in cracked:
        replace(hashdata, item, item+':'+cracked[item].strip('\n'))

print('[i] writting changes')
fout = open(HASHES+'_paired', 'w')
for item in hashdata:
    fout.write(item+'\n')
fout.close()

print('[+] done, now check "%s" [%d lines] file for results.' % (HASHES+'_paired', getlength(HASHES+'_paired')))

【问题讨论】:

  • 最好以最小的形式在此处发布您的代码(而不是 pastebin)以避免被否决并帮助那些将提供答案的人。
  • tbh,我认为没有任何方法可以提高效率。如果内存管理是问题,您可以将其拆分为几个较小的文件,但就效率而言,您无法真正提高效率。即使是可以处理大量数据的 SQL 数据库也会变慢。
  • 在迭代时写入文件,会阻止某些部分保留在内存中。
  • 好吧,我试着把它分成 100 万行然后处理,但即使算法很慢。我想知道“替换”功能是否可能是这里的问题?老实说,当我查看将修改后的数据打印到屏幕上的所谓调试会话时,它们之间几乎有 1 秒的延迟。内存在这里不是问题。我认为它实际上会加快速度,但我也意识到整个操作是单线程的(我在处理这些文件时观看了“顶部”输出)
  • @zerocool 我想了一个办法。检查我的答案。

标签: python


【解决方案1】:

有了这么多键,我强烈建议您使用某种带有 Python 的数据库来完成您的任务。使用 SQL 数据库,您将拥有两个如下所示的表:

emails_and_hashes

column_name | column_type
----------- | ------------
email       | VARCHAR(255)
----------- | ------------
hash1       | VARCHAR(255)
----------- | ------------
hash2       | VARCHAR(255)

hash1 上的索引和hash2 上的索引。

hash_to_plaintext

column_name | column_type
----------- | ------------
hash        | VARCHAR(255)
----------- | ------------
plaintext   | TEXT

hash 上的索引。

然后使用 Python DB 连接器遍历这两个表并在 Python 中更新它们的记录。这将比尝试处理dict 中的数亿条记录要快得多。您可以使用类似于以下的代码(您可能需要进行一些调整,这不是确切的答案)与此表设置、MySQL 数据库和Python MySQL Connector library:

import mysql.connector
con = mysql.connector.connect(user='your_user', password='your_password', database='your_database', host='your_host')
cur = con.cursor(dictionary=True) # 'dictionary=True' is my preference

# open your file with emails and hashes
f = open('/path/to/file1', 'r')
for line in f:
    email = line.split(':')[0]
    hash1 = line.split(':')[1]
    hash2 = line.split(':')[2]

    cur.execute("SELECT plaintext FROM hash_to_plaintext WHERE hash = %s", (hash1))
    plaintext1 = cur.fetchall()[0]
    cur.execute("SELECT plaintext FROM hash_to_plaintext WHERE hash = %s", (hash2))
    plaintext2 = cur.fetchall()[0]

    cur.execute("INSERT INTO emails_and_hashes VALUES (%s, %s, %s)", (email, hash1, hash2))

con.commit()
con.close()

【讨论】:

  • 有趣的想法。虽然我不太擅长 SQL,您能否分享一些示例查询以实现我的目标?另外,您建议如何将这么多记录加载到数据库中?
  • 两个问题,正如我在这里经常看到的那样:1) 'with open .. as ...' 在这里会更快吗? 2) 使用 executemany 怎么样?
  • 完全披露:我不知道。我可能会在with...as 上阅读其他一些问题,看看它是否更快。与executemany 相同。我知道这种方法会比只用 Python 做所有事情要快。
  • 感谢您的意见,我一定会尝试的。
【解决方案2】:

经过一天的思考,我想出了使用Trie 的想法。

trie 可以让您将重复的哈希字典存储在更高效的容器中,并以相同的成本进行查找。

PyPi 中有一个很好的 Trie 实现,称为 marisa-trie

这是我的一个想法,关于如何实现它:

import marisa_trie
import operator

with open("file2", "rb") as myfile:
    trie = marisa_trie.BytesTrie(map(operator.methodcaller("split", b":", 1), myfile))

with open("file1", "rb") as input_file, open("modified_file1", "wb") as output_file:
    for line in input_file:
        email, hash1, hash2 = line.split(b":")
        output_file.write(b":".join([email, trie[hash1], trie[hash2]]))

这应该非常快,并且内存效率比字典高 50-100 倍。

您还可以存储已处理的 trie,这样您就不需要像这样每次都重新创建它:

trie.save('my_hashes.trie')

然后像这样加载它:

trie = marisa_trie.BytesTrie()
trie.load('my_hashes.trie')

【讨论】:

  • 嗯,这是新东西,我一定会试试的!
猜你喜欢
  • 1970-01-01
  • 2022-12-17
  • 2017-09-28
  • 1970-01-01
  • 2012-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-22
相关资源
最近更新 更多