【问题标题】:Python: Constructing a unique file name for a list of filesPython:为文件列表构造一个唯一的文件名
【发布时间】:2016-05-07 07:27:22
【问题描述】:

我正在处理很多非常大的文件(例如 1000 x 512MB),我实现了一种方法来加快处理速度,方法是将某些信息写入数据库,当我重新运行软件时可以访问这些信息。出于这个原因,我需要能够为这些文件的任意子集生成唯一的文件名。

我尝试根据子集中的总文件大小和文件修改日期甚至它们的组合来生成文件名。问题是许多文件具有相同的大小和相同的修改日期,这使得我当前的标识符字符串模棱两可。重要的是,对于相同的文件列表,标识符始终相同,以便我始终可以访问相同文件的正确文件。任何想法都非常感谢!

这是我目前使用的,不起作用...

import os
import glob
import datetime

file_paths = glob.glob("path/to/files/*.foo")

def modification_date(file_path):
    return datetime.datetime.fromtimestamp(os.path.getmtime(filename=file_path))

uid = [modification_date(f) for f in file_paths]
uid = [d.year + d.day + d.day + d.hour + d.minute + d.second for d in uid]
uid = sum(uid) // len(uid) + sum([os.path.getsize(f) for f in file_paths])

【问题讨论】:

  • 如果所有元数据(大小、修改日期)都相同,则需要使用文件的内容。一个简单的 md5 哈希就可以了。
  • 这些文件有名字吗?您可以将您的子集名称基于这些文件名的组合吗?前任。由“foo.txt”和“bar.exe”组成的子集命名为"(foo.txt)(bar.exe)"
  • @Kevin 我也考虑过使用文件名,但它们很长,我最终会得到一个很长的唯一标识符。也许有一种快速的方法可以将所有文件名转换为数字序列...文件名包含各种字母和符号...
  • @Selcuk 为这么大的文件生成 md5 哈希不会需要很长时间吗?这就是为什么我一开始就避开了它。一定要快!
  • 我认为无论你做什么,你最终都会得到一个很长的唯一标识符。 1000 个文件有 2^1000 个不同的可能子集,因此根据鸽巢原则,假设您的文件名可以包含任何 128 个 ASCII字符。

标签: python


【解决方案1】:

这很可能会被淘汰,但只要列表中包含的文件没有改变,你能通过hashlib 得到你想要的吗?

import hashlib, glob
file_paths = glob.glob("/home/rolf/StackOverflow/*.py")
hash_object = hashlib.sha256(str(file_paths))
file_name= hash_object.hexdigest()
file_name
'f69dd9583eabae55319e9a56dbfc737cc16ab58634d9042f4c530e9a85e7d77c'

file_paths = glob.glob("/home/rolf/Research/*.py")
hash_object = hashlib.sha256(str(file_paths))
file_name= hash_object.hexdigest()
file_name
'dc4beec8e859b190d966068a52674d135f22520514ac1020b3e11a4af090e579'

【讨论】:

  • 嗯。这可行,但我如何确定这对于所有可能的组合都是独一无二的?
  • 正如@Michael Mullany 在附加链接中所说,两个输入之间发生冲突的概率完全取决于输入的数量,并且是 1 - (2^256)! / ((2^256^inputcount) * (2^256-inputcount)!),对于合理数量的输入,基本上为零。 stackoverflow.com/questions/2299846/…
【解决方案2】:

如果所有文件的主列表永远不会改变,那么您可以将该列表的任何子集唯一地编码为大约len(master_list)/8 字符的字符串。首先,您创建一个位数组,其中索引 i 处的 0 表示“主列表的第 i 个元素不是此子集的一部分”,而 1 表示“主列表的第 i 个元素是此子集的一部分”。然后将位数组转换为整数,然后可以将其打包成字符串。

import struct
import base64

#encodes a positive integer of any size into a string.
def string_from_int(value):
    bytes = []
    while value > 0:
        bytes.append(value % 2**8)
        value >>= 8
    return struct.pack("i"*len(bytes), *bytes)

#decodes a string into a positive integer. Only works on strings whose length is divisible by 4.
def int_from_string(s):
    bytes = struct.unpack("i" * (len(s)/4), s)
    return sum(byte * (256**i) for i, byte in enumerate(bytes))

#encodes a subset of a list into a filename-safe string.
def encode(master_file_list, subset):
    bitmask = [int(filename in subset) for filename in master_file_list]
    int_value = sum(bit*2**(i) for i, bit in enumerate(bitmask))
    string_value = string_from_int(int_value)
    #the string may contain slashes or other characters illegal in filenames, so we b64 endoce it, at the expense of increasing the length by %20 or so
    return base64.urlsafe_b64encode(string_value)

#the reverse of `encode`.
def decode(master_file_list, filename):
    string_value = base64.urlsafe_b64decode(filename)
    int_value = int_from_string(string_value)
    subset = [value for i,value in enumerate(master_file_list) if (2**i) & int_value]
    return subset

master_file_list = ['caddy.jpg', 'fjeld.dat', 'gyve.ini', 'karts.png', 'laves.txt', 'nimbi.jpg', 'ocas.ini', 'sipes.dat', 'wacky.png', 'waff.png']
subset = ["fjeld.dat", "laves.txt", "ocas.ini"]
filename = encode(master_file_list, subset)
print "Subset is:", subset
print "Filename is:", filename
print "Filename decodes back to:", decode(master_file_list, filename)

结果:

Subset is: ['fjeld.dat', 'laves.txt', 'ocas.ini']
Filename is: UgAAAA==
Filename decodes back to: ['fjeld.dat', 'laves.txt', 'ocas.ini']

【讨论】:

    猜你喜欢
    • 2013-02-27
    • 2021-04-28
    • 1970-01-01
    • 2011-12-07
    • 1970-01-01
    • 1970-01-01
    • 2013-07-11
    • 1970-01-01
    • 2012-03-31
    相关资源
    最近更新 更多