2014-01-16 96 views
0

文件結構如下所示:掃描多序列文件

folder1 
    |-----name0000.jpg 
    |-----name0000.tif 
    |-----name0001.jpg 
    |-----name0001.tif 
    |-----.... 
    |-----.... 
    |-----name2000.jpg 
    |-----name2000.tif 
    |-----name2004.tif 
    |-----.... 
    |-----name2845.tif 
    |-----other_file.txt 
    |-----folder2 
       |-----name0000.jpg 
       |-----name0000.tif 
       |-----name0001.jpg 
       |-----name0001.tif 
       |-----.... 
       |-----.... 
       |-----name2000.jpg 
       |-----name2000.tif 
       |-----other_file2.sh 

我怎樣才能讓他們變成這樣的羣體?

./folder1: name0000-2000.jpg, 340MB 
    ./folder1: name0000-2000.tif, 1GB 
    ./folder1: name2004-2845.tif, 500MB 
    ./folder1: other_file.txt, 1k 
    ./folder1/folder2: name0000-2000.jpg, 340MB 
    ./folder1/folder2: name0000-2000.tif, 1GB 
    ./folder1/folder2: other_file2.sh, 45byte 

總文件可能是幾萬,我想要速度。不僅有jpg和tif文件,也可以是其他格式。

+0

我不清楚你在問什麼。請提供更多細節。 – CrazyCasta

回答

2

使用os.walk走樹。由於這不會給你文件大小,所以你需要在每個文件上調用os.stat

接下來,顯然你想先按擴展名進行分組,然後按基本文件名(如果兩個文件名之間的唯一區別是某些數字部分關閉1,則兩個文件名一起進行分組),但按文件名對文件名進行排序。一般來說,分組最簡單的方法是對它們進行排序,然後通過鄰接功能進行分組,然後您可以隨後對其進行排序。

我不知道你的實際分組的關鍵應該是,因爲我想不出任何明智的,將自0001-2000分離2004年,但不是從2501同樣分開,我不可以肯定的是,儘管存在差距,這個規則會給你帶來什麼。所以我會把這些部分留給你。

所以:

def keyfunc(value): 
    base, ext, size = value 
    # FILL THIS IN 

def format_group(bases): 
    # FILL THIS IN 

def format_size(size): 
    # you can use inspectorG4dget's code here 

for root, dirs, names in os.walk(path): 
    sizes = (os.stat(name).st_size for name in names) 
    bases, exts = zip(*map(os.path.splitext, names)) 
    files = zip(bases, exts, sizes) 
    # now sort by ext, and then by base within each ext 
    files = sorted(files, key=operator.itemgetter(1, 0)) 
    results = [] 
    for key, group in itertools.groupby(files, key=keyfunc): 
     bases, exts, sizes = zip(*list(group)) 
     results.append((format_group(bases), sum(size)) 
    for base, size in sorted(results): 
     print('{}: {}, {}'.format(root, base, format_size(size))) 

在某些情況下,沒有明顯的分組鍵功能,但有一個明顯的方式來告訴兩個相鄰值是否算爲同一組的一部分。如果是這樣,寫,作爲一箇舊式cmp功能,如:

def keycmp(x, y): 
    if x should be in the same group as y: 
     return 0 
    return -1 

然後你可以使用在排序HOWTO描述的相同functools.cmp_to_key功能:

for key, group in itertools.groupby(files, key=cmp_to_key(keycap)): 

但是你做這可能會證明,到目前爲止,最慢的部分是在每個文件上調用stat。這是一個恥辱,因爲os.walk可能已經該統計信息,但它永遠不會把它交給你。

優化這個,你可以直接到,作爲有效給你的信息儘可能原生API。大多數現代* nix平臺(包括OS X和非古Linux)的有fts,這就好比是在C++實現的各式的os.walk,其可任選統計所有文件給你。舊的* nixes應該至少有nftwftw。Windows有FindFirstFile,這更像是一個加速版os.listdir-它爲每個文件提供各種信息,包括大小,速度非常快,但不會遞歸到子目錄中,因此您必須手動執行此操作。


如果你的比較應該使key0000.jpgkey0001.jpg相同,但不key0000.jpgkey0002.jpgkey0000.jpgkey0001.tif,顯然我們需要向下突破每名成碎片。中間一個需要轉換爲一個數字,這樣0009和0010`將會相鄰(因爲它們顯然不是字符串)。我想你想要的是這樣的:*

pattern = re.compile('(.*?)(\d+)(.*)') 
def splitname(name): 
    prefix, number, suffix = pattern.match(name).groups() 
    return prefix, int(number, 10), suffix 

因此,例如,key0000.jpg會分解成'key'0000'.jpg'。玩這個功能,並確保它正在做你真正想要的。

接下來,我們如何使用它來比較函數?那麼,它是幾乎一個正常的詞典比較,除了在中間位,如果左邊的一個比右邊少一個,它計數相等。所以:

def keycmp(a, b): 
    abits, bbits = splitname(a), splitname(b) 
    if abits[0] < bbits[0]: return -1 
    elif abits[0] > bbits[0]: return 1 
    if abits[1]+1 < bbits[1]: return -1 
    elif abits[1] > bbits[1]: return 1 
    if abits[2] < bbits[2]: return -1 
    elif abits[2] > bbits[2]: return 1 
    else: return 0 
keyfunc = functools.cmp_to_key(keycmp) 

(我們實際上並不需要從舊式cmp功能全面-1/0/1的回報,只是非零/ 0 /非零...但它一樣簡單,而且可能更具可讀性)

再次,請撥打各種文件名對keycmp,以確保他們正在做你想做的。

而你在這裏可能會需要一些錯誤處理。正如它的標準,re.match由於你給它,例如'files.txt'而無法匹配,你會得到AttributeError: 'NoneType' has no attribute 'groups'。但你應該能夠弄清楚。

最後一兩件事:我不記得,如果groupby檢查每個新值對組中的最後第一。如果是後者,這個keyfunc將不起作用。您可以嘗試編寫一個有狀態的比較器,但有一個更簡單的解決方案:groupby爲您提供了等效的Python源代碼,並且它不那麼複雜,因此您可以將其複製並粘貼到代碼中並將其更改爲記住組中最近的價值。最後,如果整個迭代器和groupby等處理都聽起來像是希臘語,那麼不要試圖在代碼運行之前對它進行轟炸。 Generator Tricks for System Programmers會教你希臘語,像這樣的一類問題將會在你的餘生中更容易。 (好吧,直到你不得不在沒有發電機另一種語言寫的...)


*我相當確保你不需要int(number, 10),因爲Python 2.7和3.x將不會將int('0123')解釋爲八進制...但是由於我必須查明它的可信度,因此明確表示它似乎是一個可讀性的好主意。

+0

嗨,abarnert:我非常感謝你的回覆,這非常有幫助,差距實際上是我的文章的一個錯字,對不起,我糾正了它。你的代碼,雖然我不完全理解它是如何工作的,因爲我是一個新的Python學習者,我需要一些時間來挖掘你的答案,我學到了很多東西。非常感謝你。 –

+0

abarnert,你會介意幫助我完成keycmp(),我想通過它們的連續性或鄰接性來對它們進行分組,我不知道如何判斷兩個字符串是否與字符串中的數字相鄰。我應該將字符串中的數字分開,然後比較它們嗎?有沒有更好的辦法?再次感謝您的幫助。 –

+0

@liaozd:是的,你可能想分開字符串中的數字。有預先製作的「自然比較」圖書館,但要解決「......和1相同」的問題,但對於那些圖書館來說,可能與編寫自己的比較一樣困難(因爲您不必像他們一樣完全一般)。看到我編輯的答案。 – abarnert

2

大部分工作是讓你的文件大小爲人類可讀的格式。看看這個工程爲你

import os 

def sizify(fpath): 
    bytes = os.stat(fpath).st_size 
    suff = 0 
    while b//1000: 
     b = b//1000 
     suff += 1 
    return str(b) + ["B", "MB", "GB" "TB"][suff] 

def humanReadable(bytes): 
    suff = 0 
    while b//1000: 
     b = b//1000 
     suff += 1 
    return str(b) + ["B", "MB", "GB" "TB"][suff]  

def getRuns(fnames): 
    fnames.sort() 
    answer = [] 
    start = fnames[0] 
    for mid,high in zip(fnames, fnames[1:]): 
     mid = int(mid.rsplit('.')[0].lstrip('name')) 
     high = int(high.rsplit('.')[0].lstrip('name')) 
     if high-mid > 1: 
      answer.append((start, mid, 
          sum(os.stat("name%s.jpg" %i).st_size for i in range(start, mid+1)) + 
          sum(os.stat("name%s.tiff" %i).st_size for i in range(start, mid+1)))) 
      start = high 
    answer.append((start, mid, 
          sum(os.stat("name%s.jpg" %i).st_size for i in range(start, mid+1)) + 
          sum(os.stat("name%s.tiff" %i).st_size for i in range(start, mid+1)))) 
    return answer 

def main(): 
    for dir, dirs, files in os.walk('folder1'): 
     runs = getRuns(files) 
     for low,high,size in runs: 
      print("%s: name%s-%s, %s" %(dir, low, high, humanReadable(size))) 

注意,這把1KB = 1000B,而不是1KB = 1024B
所以要根據你的系統,你可能要考慮改變這一點。

+0

我認爲你已經解決了這個人類可讀格式文件大小的簡單部分 - 但不是將相鄰文件組合在一起的困難部分。 – abarnert

+0

inspectorG4dget,謝謝,它有幫助。我需要將「相鄰」文件組合在一起。每個圖像文件都是電影中的幀,並且每個連續圖像序列都是一個鏡頭。 –

+0

檢查編輯。它應該可以工作,只要所有的文件都被命名爲'nameNNNN.jpg'或'nameNNNN.tiff' – inspectorG4dget

0

@abarnert:從您的博客得出下面的代碼:分組到相鄰值(鏈接:http://stupidpythonideas.blogspot.com/2014/01/grouping-into-runs-of-adjacent-values.html)的運行

我嘗試python2.6.6在Win7和CentOS的6.5 python2.6.6,問題是相同的。由於在這個python 2.6中沒有itertools.cmp_to_key(),所以我稍微修改了一下代碼,希望這個問題不是來自我的修改。

def adjacent_cmp(x, y): 
    if x+1 < y: return -1 
    elif x > y: return 1 
    else: return 0 

def cmp_to_key(mycmp): 
    'Convert a cmp= function into a key= function' 
    class K: 
     def __init__(self, obj, *args): 
      self.obj = obj 
     def __lt__(self, other): 
      return mycmp(self.obj, other.obj) < 0 
     def __gt__(self, other): 
      return mycmp(self.obj, other.obj) > 0 
     def __eq__(self, other): 
      return mycmp(self.obj, other.obj) == 0 
     def __le__(self, other): 
      return mycmp(self.obj, other.obj) <= 0 
     def __ge__(self, other): 
      return mycmp(self.obj, other.obj) >= 0 
     def __ne__(self, other): 
      return mycmp(self.obj, other.obj) != 0 
    return K 

class groupby: 
    # [k for k, g in groupby('AAAABBBCCDAABBB')] --> A B C D A B 
    # [list(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D 
    def __init__(self, iterable, key=None): 
     if key is None: 
      key = lambda x: x 
     self.keyfunc = key 
     self.it = iter(iterable) 
     self.sentinel = self.tgtkey = self.currkey = self.currvalue = object() 
    def __iter__(self): 
     return self 
    def next(self): 
     while (self.currkey is self.sentinel 
       or self.tgtkey is not self.sentinel 
       and self.tgtkey == self.currkey): 
      self.currvalue = next(self.it) # Exit on StopIteration 
      self.currkey = self.keyfunc(self.currvalue) 
     self.tgtkey = self.currkey 
     return (self.currkey, self._grouper(self.tgtkey)) 
    def _grouper(self, tgtkey): 
     while tgtkey is self.sentinel or tgtkey == self.currkey: 
      yield self.currvalue 
      self.currvalue = next(self.it) # Exit on StopIteration 
      tgtkey, self.currkey = self.currkey, self.keyfunc(self.currvalue) 

adjacent_key = cmp_to_key(adjacent_cmp) 
a = [0, 1, 2] 
print [list(g) for k, g in groupby(a, adjacent_key)] 

[[0, 1, 2], [2]] 
+0

好的,問題是來自docs的純Python'groupby'將'tgtkey'作爲參數傳遞給'_grouper',而不是'_grouper'使用該屬性。這似乎只不過是微優化(局部變量查找比實例屬性查找更快),這幾乎不會有關係。這意味着我們最終測試一個組的最後一個元素來決定是否完成,所以如果最後一個元素在一個大於2的組中,它最終會重複。讓我編輯博客文章;感謝抓住這一點。 – abarnert

+0

但是,你不應該使用hack-up'groupby'。正如博客文章解釋的那樣,在實現這兩種方式之前似乎更簡單,但編寫一個可以與標準'groupby'配合使用的關鍵函數變得更簡單和更清晰。 (另外,注意:將代碼從3.x移植到2.x時,總是將'class foo:'更改爲'class foo(object):'或者獲得舊式類。)最後,發佈一個答案像這是真的濫用SO系統。如果發表評論時有鏈接到pastebin,或對我的博客文章發表評論,或發表一個新問題或其他任何內容,本來會更好。 – abarnert