2015-06-29 9 views
10

का उपयोग कर कार्यक्रम मैं बनाए रखने के उस में के रूप में किया जाता है में एक संग्रह में फ़ाइलों की संख्या गिनती करने के लिए:कैसे प्रोग्राम के रूप में अजगर

# count the files in the archive 
length = 0 
command = ur'"%s" l -slt "%s"' % (u'path/to/7z.exe', srcFile) 
ins, err = Popen(command, stdout=PIPE, stdin=PIPE, 
       startupinfo=startupinfo).communicate() 
ins = StringIO.StringIO(ins) 
for line in ins: length += 1 
ins.close() 
  1. यह वास्तव में एक ही तरीका है? मुझे any other command नहीं मिल रहा है लेकिन ऐसा लगता है कि मैं सिर्फ
  2. त्रुटियों की जांच के बारे में क्या कह सकता हूं? क्या इसे संशोधित करने के लिए पर्याप्त होगा:

    proc = Popen(command, stdout=PIPE, stdin=PIPE, 
          startupinfo=startupinfo) 
    out = proc.stdout 
    # ... count 
    returncode = proc.wait() 
    if returncode: 
        raise Exception(u'Failed reading number of files from ' + srcFile) 
    

    या क्या मुझे वास्तव में पॉपन के आउटपुट का विश्लेषण करना चाहिए?

संपादित करें: 7z में रुचि, rar, ज़िप अभिलेखागार (कि 7z.exe द्वारा समर्थित हैं) - लेकिन 7z और ज़िप शुरुआत

+1

कौन सा संग्रह के प्रकार आप करने वाले हैं समर्थन? –

+1

ज़िप के लिए, टैर चेक https://docs.python.org/2/library/zipfile.html और https://docs.python.org/2/library/tarfile.html –

+0

@ LoïcFaure-Lacroix: धन्यवाद - संपादित किया गया। मुझे निश्चित रूप से 7z की आवश्यकता है ... –

उत्तर

7

के लिए पर्याप्त एक ज़िप संग्रह के सदस्यों की संख्या की गणना करने के लिए किया जाएगा अजगर में संग्रह:

#!/usr/bin/env python 
import sys 
from contextlib import closing 
from zipfile import ZipFile 

with closing(ZipFile(sys.argv[1])) as archive: 
    count = len(archive.infolist()) 
print(count) 

यह, यदि उपलब्ध zlib, bz2, lzma मॉड्यूल का उपयोग संग्रह को संपीड़ित कर सकते हैं।


एक tar संग्रह में नियमित रूप से फ़ाइलों की संख्या की गणना करने के लिए:

#!/usr/bin/env python 
import sys 
import tarfile 

with tarfile.open(sys.argv[1]) as archive: 
    count = sum(1 for member in archive if member.isreg()) 
print(count) 

यह अजगर के संस्करण के आधार gzip, bz2 और lzma संपीड़न समर्थन कर सकते हैं।

आपको एक तृतीय-पक्ष मॉड्यूल मिल सकता है जो 7z अभिलेखागार के लिए समान कार्यक्षमता प्रदान करेगा।


7z सुविधा का उपयोग भी एक संग्रह में फ़ाइलों की संख्या प्राप्त करने के लिए:

import os 
import subprocess 

def count_files_7z(archive): 
    s = subprocess.check_output(["7z", "l", archive], env=dict(os.environ, LC_ALL="C")) 
    return int(re.search(br'(\d+)\s+files,\s+\d+\s+folders$', s).group(1)) 

यहाँ संस्करण है कि कम स्मृति का उपयोग कर सकते अगर वहाँ संग्रह में कई फाइलों हैं:

import os 
import re 
from subprocess import Popen, PIPE, CalledProcessError 

def count_files_7z(archive): 
    command = ["7z", "l", archive] 
    p = Popen(command, stdout=PIPE, bufsize=1, env=dict(os.environ, LC_ALL="C")) 
    with p.stdout: 
     for line in p.stdout: 
      if line.startswith(b'Error:'): # found error 
       error = line + b"".join(p.stdout) 
       raise CalledProcessError(p.wait(), command, error) 
    returncode = p.wait() 
    assert returncode == 0 
    return int(re.search(br'(\d+)\s+files,\s+\d+\s+folders', line).group(1)) 

उदाहरण:

import sys 

try: 
    print(count_files_7z(sys.argv[1])) 
except CalledProcessError as e: 
    getattr(sys.stderr, 'buffer', sys.stderr).write(e.output) 
    sys.exit(e.returncode) 

एक सामान्य उपप्रक्रिया के उत्पादन में लाइनों की संख्या की गणना करने के लिए:

from functools import partial 
from subprocess import Popen, PIPE, CalledProcessError 

p = Popen(command, stdout=PIPE, bufsize=-1) 
with p.stdout: 
    read_chunk = partial(p.stdout.read, 1 << 15) 
    count = sum(chunk.count(b'\n') for chunk in iter(read_chunk, b'')) 
if p.wait() != 0: 
    raise CalledProcessError(p.returncode, command) 
print(count) 

यह असीमित उत्पादन का समर्थन करता है।


Could you explain why buffsize=-1 (as opposed to buffsize=1 in your previous answer: stackoverflow.com/a/30984882/281545)

bufsize=-1 साधन bufsize=0 (unbuffered) के बजाय डिफ़ॉल्ट आई/ओ बफर आकार अजगर 2. पर यह अजगर 2. पर एक प्रदर्शन को बढ़ावा देने यह हाल अजगर 3 संस्करणों पर डिफ़ॉल्ट है का उपयोग करें। यदि आपको कुछ पहले पायथन 3 संस्करणों पर bufsizebufsize=-1 में बदला नहीं गया है, तो आपको एक संक्षिप्त पढ़ा जा सकता है (डेटा खोना)।

यह उत्तर भाग में पढ़ता है और इसलिए स्ट्रीम पूरी तरह से दक्षता के लिए buffered है। The solution you've linked लाइन उन्मुख है। bufsize=1 का मतलब है "लाइन buffered"। अन्यथा bufsize=-1 से न्यूनतम अंतर होता है।

and also what the read_chunk = partial(p.stdout.read, 1 << 15) buys us ?

यह read_chunk = lambda: p.stdout.read(1<<15) के बराबर है, लेकिन सामान्य रूप में और अधिक आत्मनिरीक्षण प्रदान करता है। इसका उपयोग implement wc -l in Python efficiently पर किया जाता है।

+0

अरे धन्यवाद! क्या आप समझा सकते हैं कि buffsize = -1 (जैसा कि आपके पिछले उत्तर में buffsize = 1 के विपरीत है: http://stackoverflow.com/a/30984882/281545) - और यह भी 'read_chunk = partial (p.stdout.read, 1 << 15) 'हमें खरीदता है? वास्तव में यह 'buffsize' मेरे लिए एक रहस्य है (और मेरे Google प्रयासों के लिए)। इस बीच से मेरे पास पहले से ही '7z.exe' बंडल है (और मैं सही त्रुटि प्रदर्शित करना चाहता हूं) मुझे लगता है कि मैं अपने उत्तर के साथ जाऊंगा (सिवाय इसके कि अगर मैंने कुछ भी बेवकूफ बना दिया है) –

+0

@Mr_and_Mrs_D: आपको शायद इसके बारे में पूछना चाहिए एक अलग प्रश्न के रूप में '7z.exe' में त्रुटि प्रबंधन: निम्न शामिल करें:' 7z' जैसे विभिन्न त्रुटियों को इंगित करने के लिए निकास कोड का एक पहुंच सेट प्रदान करता है जैसे कि ['ज़िप 'उपयोगिता करता है] (http: //linux.die .net/आदमी/1/ज़िप)? क्या '7z' अपने त्रुटि संदेशों को stderr पर प्रिंट करता है या क्या यह उन्हें stdout में संग्रह सदस्य सूची के साथ मिलाता है? – jfs

+0

जब मुझे कुछ समय मिलेगा और आपको उल्लेख करना सुनिश्चित होगा - धन्यवाद :) - निकास कोड: http://sevenzip.osdn.jp/chm/cmdline/exit_codes.htm –

1

जब से मैं पहले से ही एप्लिकेशन के साथ बंडल 7z.exe है और मैं निश्चित रूप से एक तीसरी पार्टी lib से बचने के लिए, जब तक मैं rar और 7z अभिलेखागार मुझे लगता है कि मैं जाना होगा पार्स करने के लिए की जरूरत करना चाहती के साथ:

regErrMatch = re.compile(u'Error:', re.U).match # needs more testing 
r"""7z list command output is of the form: 
    Date  Time Attr   Size Compressed Name 
------------------- ----- ------------ ------------ ------------------------ 
2015-06-29 21:14:04 ....A  <size>    <filename> 
where ....A is the attribute value for normal files, ....D for directories 
""" 
reFileMatch = re.compile(ur'(\d|:|-|\s)*\.\.\.\.A', re.U).match 

def countFilesInArchive(srcArch, listFilePath=None): 
    """Count all regular files in srcArch (or only the subset in 
    listFilePath).""" 
    # https://stackoverflow.com/q/31124670/281545 
    command = ur'"%s" l -scsUTF-8 -sccUTF-8 "%s"' % ('compiled/7z.exe', srcArch) 
    if listFilePath: command += u' @"%s"' % listFilePath 
    proc = Popen(command, stdout=PIPE, startupinfo=startupinfo, bufsize=-1) 
    length, errorLine = 0, [] 
    with proc.stdout as out: 
     for line in iter(out.readline, b''): 
      line = unicode(line, 'utf8') 
      if errorLine or regErrMatch(line): 
       errorLine.append(line) 
      elif reFileMatch(line): 
       length += 1 
    returncode = proc.wait() 
    if returncode or errorLine: raise StateError(u'%s: Listing failed\n' + 
     srcArch + u'7z.exe return value: ' + str(returncode) + 
     u'\n' + u'\n'.join([x.strip() for x in errorLine if x.strip()])) 
    return length 

@JFSebastien


मेरे अंतिम (ish) स्वीकार कर लिया उत्तर के आधार पर द्वारा Python Popen - wait vs communicate vs CalledProcessError में के रूप में देखने में त्रुटि - यूनिकोड की जरूरत नहीं किया जा सकता है, अब मैं इसे हर जगह का उपयोग के रूप में के लिए यह रखा। इसके अलावा regex रखा (जो मैं विस्तार हो सकता है, मैं चीजों re.compile(u'^(Error:.+|.+ Data Error?|Sub items Errors:.+)',re.U) की तरह देखा है। Check_output और CalledProcessError में देखना होगा।

def countFilesInArchive(srcArch, listFilePath=None): 
    """Count all regular files in srcArch (or only the subset in 
    listFilePath).""" 
    command = [exe7z, u'l', u'-scsUTF-8', u'-sccUTF-8', srcArch] 
    if listFilePath: command += [u'@%s' % listFilePath] 
    proc = Popen(command, stdout=PIPE, stdin=PIPE, # stdin needed if listFilePath 
       startupinfo=startupinfo, bufsize=1) 
    errorLine = line = u'' 
    with proc.stdout as out: 
     for line in iter(out.readline, b''): # consider io.TextIOWrapper 
      line = unicode(line, 'utf8') 
      if regErrMatch(line): 
       errorLine = line + u''.join(out) 
       break 
    returncode = proc.wait() 
    msg = u'%s: Listing failed\n' % srcArch.s 
    if returncode or errorLine: 
     msg += u'7z.exe return value: ' + str(returncode) + u'\n' + errorLine 
    elif not line: # should not happen 
     msg += u'Empty output' 
    else: msg = u'' 
    if msg: raise StateError(msg) # consider using CalledProcessError 
    # number of files is reported in the last line - example: 
    #        3534900  325332 75 files, 29 folders 
    return int(re.search(ur'(\d+)\s+files,\s+\d+\s+folders', line).group(1)) 

मेरे निष्कर्ष के साथ इस संपादित करेंगे।

+1

आप io.TextIOWrapper (आउट, एन्कोडिंग = 'utf-8') में लाइन के लिए 'यहां या बेहतर' के लिए 'लाइन या आउट' का उपयोग कर सकते हैं: '(यूनिकोड को बाइट्स को डीकोड करने और सार्वभौमिक न्यूलाइन मोड को सक्षम करने के लिए)। 'यदि लेन (कंटेनर) 'का उपयोग न करें, तो इसके बजाय' कंटेनर 'का उपयोग करें (खाली कंटेनर पाइथन में गलत हैं)। 'regErrMatch' regex के बजाय 'line.startswith (' त्रुटि: ')' का उपयोग किया जा सकता है। क्या आप सुनिश्चित हैं कि '7z' अपनी त्रुटियों को stdout प्रिंट करता है (यह दुर्भाग्यपूर्ण है)? कृपया, [पेप -8 नामकरण सम्मेलनों का पालन करें जब तक आपके पास कोई विशिष्ट कारण न हो] (https://www.python.org/dev/peps/pep-0008/#naming-conventions)। – jfs

+0

हां 7z stdout (...) में इसके आउटपुट को प्रिंट करता है - TextIOWrapper मैं एक नज़र रखूंगा। regErrMatch: मुझे त्रुटियों के लिए नियमित अभिव्यक्ति पर विस्तार करने की आवश्यकता हो सकती है। पीईपी 8 - यह विरासत कोड है, धीरे-धीरे पीईपी 8 इसे (यह भी देखें: https://www.youtube.com/watch?v=wf-BqAjZb8M - हालांकि 79 वर्ण, मैं पूरी तरह से समझौते में हूं) –

संबंधित मुद्दे