python學習-06(模塊化編程)


 

1:模塊定義

         模塊:用來從邏輯上組織Python代碼(變量,函數、類,邏輯:實現一個功能),本質就是.py結尾的Python文件(文件名:test.py,對應的模塊名,test)。

         包:本質就是一個文件夾或者是目錄(必須帶有一個_init_.py文件),包是用來從邏輯上組織模塊的。

2.導入方法:

1):import module_test, module_test1 (調用方法的時候,需要module_test.   )

2):form moudule_test import * (* 表示所有的方法)(不建議使用,這個就相當於導入了程序。)  容易造成覆蓋,出現問題。

3):form module_test import module_ajun  as module_ajun_test(作為一個別名來使用)

3:import本質 (路徑 搜索和搜索路徑)

1)      import module_test  相當於把module_test 的所有代碼解釋一遍,全部傳遞給module_test. 調用module_test的變量和方法。

2)      form moudule_test import name 相當於值調用了name變量,在當前模塊,就不需要加前綴了。

總結:導入模塊的本質就是把Python文件解釋一遍。(import test  test=’test.py  all code’)

           導入包的本質就是執行包下面的_init_.py文件

            Import module_name--》mudule_name.py--》module_name.py的路徑--》sys.path

 

 

os.path.abspath(_file_)=獲取 當前目錄

os.path.dirname(os.path.abspath(_file_))=獲取當前目錄的父目錄

os.path.dirname(os.path.dirname (os.path.abspath(_file_)))=獲取當前目錄的父目錄 的父目錄

form .(點表示當前目錄,就是_init_的目錄)  import 包名

4.導入優化

        

怎么優化?每次都去找,很浪費時間?

使用from module_test  import test 這個方法,節約時間。

5.模塊的分類

A:標准庫

B:開源模塊

C:自定義模塊

 

A標准庫

5.1time與datetime 兩個庫。

1)表示時間,格式化的時間格式:2017-07-28

2)時間戳:從1970.1.1日到現在總共經過了多少秒?

為什么從1970年1月1日開始呢?

3)元祖,共有九個元素表示。


UTC(Coordinated Universal Time,世界協調時)亦即格林威治天文時間,世界標准時間。在中國為UTC+8。DST(Daylight Saving Time)即夏令時。 

時間戳(timestamp)的方式:通常來說,時間戳表示的是從1970年1月1日00:00:00開始按秒計算的偏移量。我們運行“type(time.time())”,返回的是float類型。返回時間戳方式的函數主要有time(),clock()等。 

元組(struct_time)方式:struct_time元組共有9個元素,返回struct_time的函數主要有gmtime(),localtime(),strptime()。下面列出這種方式元組中的幾個元素: 

 1 #Author:ajun
 2 import time
 3 
 4 #print(time.process_time())#測量處理器運算時間,不包括sleep時間,不穩定
 5 #print(time.altzone)  #返回與utc時間的時間差,以秒計算\
 6 #print(time.asctime()) #返回時間格式Wed Jun 28 21:38:29 2017 格式,
 7 #print(time.localtime()) #返回本地時間 的struct time對象格式
 8 #print(time.gmtime(time.time()-800000)) #返回utc時間的struc時間對象格式
 9 #print(time.asctime(time.localtime())) #返回時間格式"Fri Aug 19 11:14:16 2016",
10 #print(time.ctime()) #返回Wed Jun 28 21:38:29 2017 格式, 同上
11 # 日期字符串 轉成  時間戳
12 string_2_struct = time.strptime("2017/06/28","%Y/%m/%d") #將 日期字符串 轉成 struct時間對象格式
13 print(string_2_struct)
14 # #
15 struct_2_stamp = time.mktime(string_2_struct) #將struct時間對象轉成時間戳
16 print(struct_2_stamp)
17 
18 #將時間戳轉為字符串格式
19 print(time.gmtime(time.time()-86640)) #將utc時間戳轉換成struct_time格式
20 print(time.strftime("%Y-%m-%d %H:%M:%S",time.gmtime()) ) #將utc struct_time格式轉成指定的字符串格式
21 
22 #時間加減
23 import datetime
24 
25 print(datetime.datetime.now()) #返回 2017-06-28 21:11:01.433280
26 print(datetime.date.fromtimestamp(time.time()) )  # 時間戳直接轉成日期格式 2017-06-28
27 # print(datetime.datetime.now() )
28 # print(datetime.datetime.now() + datetime.timedelta(3)) #當前時間+3天
29 # print(datetime.datetime.now() + datetime.timedelta(-3)) #當前時間-3天
30 # print(datetime.datetime.now() + datetime.timedelta(hours=3)) #當前時間+3小時
31 # print(datetime.datetime.now() + datetime.timedelta(minutes=30)) #當前時間+30分
32 
33 #修改時間
34 c_time  = datetime.datetime.now()
35 print(c_time.replace(minute=3,hour=2)) #時間替換)) #時間替換
時間模塊

時間轉換圖(一個來自alex,一個來自海峰老師)

 

%a    本地(locale)簡化星期名稱    
%A    本地完整星期名稱    
%b    本地簡化月份名稱    
%B    本地完整月份名稱    
%c    本地相應的日期和時間表示    
%d    一個月中的第幾天(01 - 31%H    一天中的第幾個小時(24小時制,00 - 23%I    第幾個小時(12小時制,01 - 12%j    一年中的第幾天(001 - 366%m    月份(01 - 12%M    分鍾數(00 - 59%p    本地am或者pm的相應符    一    
%S    秒(01 - 61)    二    
%U    一年中的星期數。(00 - 53星期天是一個星期的開始。)第一個星期天之前的所有天數都放在第0周。    三    
%w    一個星期中的第幾天(0 - 6,0是星期天)    三    
%W    和%U基本相同,不同的是%W以星期一為一個星期的開始。    
%x    本地相應日期    
%X    本地相應時間    
%y    去掉世紀的年份(00 - 99%Y    完整的年份    
%Z    時區的名字(如果不存在為空字符)    
%%    ‘%’字符

 5.2random模塊

隨機數

import random
print (random.random())  #0.6445010863311293  
#random.random()用於生成一個0到1的隨機符點數: 0 <= n < 1.0
print (random.randint(1,7)) #4
#random.randint()的函數原型為:random.randint(a, b),用於生成一個指定范圍內的整數。
# 其中參數a是下限,參數b是上限,生成的隨機數n: a <= n <= b
print (random.randrange(1,10)) #5
#random.randrange的函數原型為:random.randrange([start], stop[, step]),
# 從指定范圍內,按指定基數遞增的集合中 獲取一個隨機數。如:random.randrange(10, 100, 2),
# 結果相當於從[10, 12, 14, 16, ... 96, 98]序列中獲取一個隨機數。
# random.randrange(10, 100, 2)在結果上與 random.choice(range(10, 100, 2) 等效。
print(random.choice('liukuni')) #i
#random.choice從序列中獲取一個隨機元素。
# 其函數原型為:random.choice(sequence)。參數sequence表示一個有序類型。
# 這里要說明一下:sequence在python不是一種特定的類型,而是泛指一系列的類型。
# list, tuple, 字符串都屬於sequence。有關sequence可以查看python手冊數據模型這一章。
# 下面是使用choice的一些例子:
print(random.choice("學習Python"))#
print(random.choice(["JGood","is","a","handsome","boy"]))  #List
print(random.choice(("Tuple","List","Dict")))   #List
print(random.sample([1,2,3,4,5],3))    #[1, 2, 5]
#random.sample的函數原型為:random.sample(sequence, k),從指定序列中隨機獲取指定長度的片斷。sample函數不會修改原有序列。
#!/usr/bin/env python
# encoding: utf-8
import random
import string
#隨機整數:
print( random.randint(0,99))  #70
 
#隨機選取0到100間的偶數:
print(random.randrange(0, 101, 2)) #4
 
#隨機浮點數:
print( random.random()) #0.2746445568079129
print(random.uniform(1, 10)) #9.887001463194844
 
#隨機字符:
print(random.choice('abcdefg&#%^*f')) #f
 
#多個字符中選取特定數量的字符:
print(random.sample('abcdefghij',3)) #['f', 'h', 'd']
 
#隨機選取字符串:
print( random.choice ( ['apple', 'pear', 'peach', 'orange', 'lemon'] )) #apple
#洗牌#
items = [1,2,3,4,5,6,7]
print(items) #[1, 2, 3, 4, 5, 6, 7]
random.shuffle(items)
print(items) #[1, 4, 7, 2, 5, 3, 6]
應用實例
 1 import random
 2 checkcode=''
 3 for i in range(5):
 4     current_a=random.randint(1,6)
 5     if current_a%2:
 6         temp = chr(random.randint(65,90))
 7     else:
 8         temp = random.randint(0,9)
 9     checkcode+=str(temp)
10 print(checkcode)
隨機生成驗證碼的例子

 5.3os模塊

參考:https://docs.python.org/2/library/os.html?highlight=os#module-os (幫助)

http://www.cnblogs.com/alex3714/articles/5161349.html(alex)

http://egon09.blog.51cto.com/9161406/1840425(海峰老師)

#Author:ajun
os.getcwd() #獲取當前工作目錄,即當前python腳本工作的目錄路徑
os.chdir("dirname")  #改變當前腳本工作目錄;相當於shell下cd
os.curdir # 返回當前目錄: ('.')
os.pardir # 獲取當前目錄的父目錄字符串名:('..')
os.makedirs('dirname1/dirname2')    #可生成多層遞歸目錄
os.removedirs('dirname1')  #  若目錄為空,則刪除,並遞歸到上一級目錄,如若也為空,則刪除,依此類推
os.mkdir('dirname')   # 生成單級目錄;相當於shell中mkdir dirname
os.rmdir('dirname')   # 刪除單級空目錄,若目錄不為空則無法刪除,報錯;相當於shell中rmdir dirname
os.listdir('dirname')    #列出指定目錄下的所有文件和子目錄,包括隱藏文件,並以列表方式打印
os.remove()  #刪除一個文件
os.rename("oldname","newname")  #重命名文件/目錄
os.stat('path/filename')  獲取文件/目錄信息
os.sep    #輸出操作系統特定的路徑分隔符,win下為"\\",Linux下為"/"
os.linesep    #輸出當前平台使用的行終止符,win下為"\t\n",Linux下為"\n"
os.pathsep   # 輸出用於分割文件路徑的字符串
os.name   # 輸出字符串指示當前使用平台。win->'nt'; Linux->'posix'
os.system("bash command")  #運行shell命令,直接顯示
os.environ # 獲取系統環境變量
os.path.abspath(path)  #返回path規范化的絕對路徑
os.path.split(path)  #將path分割成目錄和文件名二元組返回
os.path.dirname(path)  #返回path的目錄。其實就是os.path.split(path)的第一個元素
os.path.basename(path)  #返回path最后的文件名。如何path以/或\結尾,那么就會返回空值。即os.path.split(path)的第二個元素
os.path.exists(path)  #如果path存在,返回True;如果path不存在,返回False
os.path.isabs(path)  #如果path是絕對路徑,返回True
os.path.isfile(path)  #如果path是一個存在的文件,返回True。否則返回False
os.path.isdir(path)  #如果path是一個存在的目錄,則返回True。否則返回False
os.path.join(path1[,path2[, ...]]) # 將多個路徑組合后返回,第一個絕對路徑之前的參數將被忽略
os.path.getatime(path)  #返回path所指向的文件或者目錄的最后存取時間
os.path.getmtime(path)  #返回path所指向的文件或者目錄的最后修改時間

5.4sys模塊

1 sys.argv           #命令行參數List,第一個元素是程序本身路徑
2 sys.exit(n)       # 退出程序,正常退出時exit(0)
3 sys.version        #獲取Python解釋程序的版本信息
4 sys.maxint         #最大的Int值
5 sys.path           返回模塊的搜索路徑,初始化時使用PYTHONPATH環境變量的值#
6 sys.platform     #  返回操作系統平台名稱
7 sys.stdout.write('please:')  #寫進度條就需要這個函數
8 val = sys.stdin.readline()[:-1]

5.5shutil模塊

主要就是用於文件的拷貝和壓縮包。

參考http://www.cnblogs.com/wupeiqi/articles/4963027.html 

高級的 文件、文件夾、壓縮包 處理模塊

shutil.copyfileobj(fsrc, fdst[, length])
將文件內容拷貝到另一個文件中,可以部分內容

1 def copyfileobj(fsrc, fdst, length=16*1024):
2     """copy data from file-like object fsrc to file-like object fdst"""
3     while 1:
4         buf = fsrc.read(length)
5         if not buf:
6             break
7         fdst.write(buf)

shutil.copyfile(src, dst)
拷貝文件

def copyfile(src, dst):
    """Copy data from src to dst"""
    if _samefile(src, dst):
        raise Error("`%s` and `%s` are the same file" % (src, dst))

    for fn in [src, dst]:
        try:
            st = os.stat(fn)
        except OSError:
            # File most likely does not exist
            pass
        else:
            # XXX What about other special files? (sockets, devices...)
            if stat.S_ISFIFO(st.st_mode):
                raise SpecialFileError("`%s` is a named pipe" % fn)

    with open(src, 'rb') as fsrc:
        with open(dst, 'wb') as fdst:
            copyfileobj(fsrc, fdst)

shutil.copymode(src, dst)
僅拷貝權限。內容、組、用戶均不變

def copymode(src, dst):
    """Copy mode bits from src to dst"""
    if hasattr(os, 'chmod'):
        st = os.stat(src)
        mode = stat.S_IMODE(st.st_mode)
        os.chmod(dst, mode)

shutil.copystat(src, dst)
拷貝狀態的信息,包括:mode bits, atime, mtime, flags

def copystat(src, dst):
    """Copy all stat info (mode bits, atime, mtime, flags) from src to dst"""
    st = os.stat(src)
    mode = stat.S_IMODE(st.st_mode)
    if hasattr(os, 'utime'):
        os.utime(dst, (st.st_atime, st.st_mtime))
    if hasattr(os, 'chmod'):
        os.chmod(dst, mode)
    if hasattr(os, 'chflags') and hasattr(st, 'st_flags'):
        try:
            os.chflags(dst, st.st_flags)
        except OSError, why:
            for err in 'EOPNOTSUPP', 'ENOTSUP':
                if hasattr(errno, err) and why.errno == getattr(errno, err):
                    break
            else:
                raise

shutil.copy(src, dst)
拷貝文件和權限

def copy(src, dst):
    """Copy data and mode bits ("cp src dst").

    The destination may be a directory.

    """
    if os.path.isdir(dst):
        dst = os.path.join(dst, os.path.basename(src))
    copyfile(src, dst)
    copymode(src, dst)

shutil.copy2(src, dst)
拷貝文件和狀態信息

 1 def copy2(src, dst):
 2     """Copy data and all stat info ("cp -p src dst").
 3 
 4     The destination may be a directory.
 5 
 6     """
 7     if os.path.isdir(dst):
 8         dst = os.path.join(dst, os.path.basename(src))
 9     copyfile(src, dst)
10     copystat(src, dst)

shutil.ignore_patterns(*patterns)
shutil.copytree(src, dst, symlinks=False, ignore=None)
遞歸的去拷貝文件

例如:copytree(source, destination, ignore=ignore_patterns('*.pyc', 'tmp*'))

 1 def ignore_patterns(*patterns):
 2     """Function that can be used as copytree() ignore parameter.
 3 
 4     Patterns is a sequence of glob-style patterns
 5     that are used to exclude files"""
 6     def _ignore_patterns(path, names):
 7         ignored_names = []
 8         for pattern in patterns:
 9             ignored_names.extend(fnmatch.filter(names, pattern))
10         return set(ignored_names)
11     return _ignore_patterns
12 
13 def copytree(src, dst, symlinks=False, ignore=None):
14     """Recursively copy a directory tree using copy2().
15 
16     The destination directory must not already exist.
17     If exception(s) occur, an Error is raised with a list of reasons.
18 
19     If the optional symlinks flag is true, symbolic links in the
20     source tree result in symbolic links in the destination tree; if
21     it is false, the contents of the files pointed to by symbolic
22     links are copied.
23 
24     The optional ignore argument is a callable. If given, it
25     is called with the `src` parameter, which is the directory
26     being visited by copytree(), and `names` which is the list of
27     `src` contents, as returned by os.listdir():
28 
29         callable(src, names) -> ignored_names
30 
31     Since copytree() is called recursively, the callable will be
32     called once for each directory that is copied. It returns a
33     list of names relative to the `src` directory that should
34     not be copied.
35 
36     XXX Consider this example code rather than the ultimate tool.
37 
38     """
39     names = os.listdir(src)
40     if ignore is not None:
41         ignored_names = ignore(src, names)
42     else:
43         ignored_names = set()
44 
45     os.makedirs(dst)
46     errors = []
47     for name in names:
48         if name in ignored_names:
49             continue
50         srcname = os.path.join(src, name)
51         dstname = os.path.join(dst, name)
52         try:
53             if symlinks and os.path.islink(srcname):
54                 linkto = os.readlink(srcname)
55                 os.symlink(linkto, dstname)
56             elif os.path.isdir(srcname):
57                 copytree(srcname, dstname, symlinks, ignore)
58             else:
59                 # Will raise a SpecialFileError for unsupported file types
60                 copy2(srcname, dstname)
61         # catch the Error from the recursive copytree so that we can
62         # continue with other files
63         except Error, err:
64             errors.extend(err.args[0])
65         except EnvironmentError, why:
66             errors.append((srcname, dstname, str(why)))
67     try:
68         copystat(src, dst)
69     except OSError, why:
70         if WindowsError is not None and isinstance(why, WindowsError):
71             # Copying file access times may fail on Windows
72             pass
73         else:
74             errors.append((src, dst, str(why)))
75     if errors:
76         raise Error, errors
View Code

shutil.rmtree(path[, ignore_errors[, onerror]])
遞歸的去刪除文件

 1 def rmtree(path, ignore_errors=False, onerror=None):
 2     """Recursively delete a directory tree.
 3 
 4     If ignore_errors is set, errors are ignored; otherwise, if onerror
 5     is set, it is called to handle the error with arguments (func,
 6     path, exc_info) where func is os.listdir, os.remove, or os.rmdir;
 7     path is the argument to that function that caused it to fail; and
 8     exc_info is a tuple returned by sys.exc_info().  If ignore_errors
 9     is false and onerror is None, an exception is raised.
10 
11     """
12     if ignore_errors:
13         def onerror(*args):
14             pass
15     elif onerror is None:
16         def onerror(*args):
17             raise
18     try:
19         if os.path.islink(path):
20             # symlinks to directories are forbidden, see bug #1669
21             raise OSError("Cannot call rmtree on a symbolic link")
22     except OSError:
23         onerror(os.path.islink, path, sys.exc_info())
24         # can't continue even if onerror hook returns
25         return
26     names = []
27     try:
28         names = os.listdir(path)
29     except os.error, err:
30         onerror(os.listdir, path, sys.exc_info())
31     for name in names:
32         fullname = os.path.join(path, name)
33         try:
34             mode = os.lstat(fullname).st_mode
35         except os.error:
36             mode = 0
37         if stat.S_ISDIR(mode):
38             rmtree(fullname, ignore_errors, onerror)
39         else:
40             try:
41                 os.remove(fullname)
42             except os.error, err:
43                 onerror(os.remove, fullname, sys.exc_info())
44     try:
45         os.rmdir(path)
46     except os.error:
47         onerror(os.rmdir, path, sys.exc_info())
View Code

shutil.move(src, dst)
遞歸的去移動文件

def move(src, dst):
    """Recursively move a file or directory to another location. This is
    similar to the Unix "mv" command.

    If the destination is a directory or a symlink to a directory, the source
    is moved inside the directory. The destination path must not already
    exist.

    If the destination already exists but is not a directory, it may be
    overwritten depending on os.rename() semantics.

    If the destination is on our current filesystem, then rename() is used.
    Otherwise, src is copied to the destination and then removed.
    A lot more could be done here...  A look at a mv.c shows a lot of
    the issues this implementation glosses over.

    """
    real_dst = dst
    if os.path.isdir(dst):
        if _samefile(src, dst):
            # We might be on a case insensitive filesystem,
            # perform the rename anyway.
            os.rename(src, dst)
            return

        real_dst = os.path.join(dst, _basename(src))
        if os.path.exists(real_dst):
            raise Error, "Destination path '%s' already exists" % real_dst
    try:
        os.rename(src, real_dst)
    except OSError:
        if os.path.isdir(src):
            if _destinsrc(src, dst):
                raise Error, "Cannot move a directory '%s' into itself '%s'." % (src, dst)
            copytree(src, real_dst, symlinks=True)
            rmtree(src)
        else:
            copy2(src, real_dst)
            os.unlink(src)

shutil.make_archive(base_name, format,...)

創建壓縮包並返回文件路徑,例如:zip、tar

  • base_name: 壓縮包的文件名,也可以是壓縮包的路徑。只是文件名時,則保存至當前目錄,否則保存至指定路徑,
    如:www                        =>保存至當前路徑
    如:/Users/wupeiqi/www =>保存至/Users/wupeiqi/
  • format: 壓縮包種類,“zip”, “tar”, “bztar”,“gztar”
  • root_dir: 要壓縮的文件夾路徑(默認當前目錄)
  • owner: 用戶,默認當前用戶
  • group: 組,默認當前組
  • logger: 用於記錄日志,通常是logging.Logger對象
  • 1 #將 /Users/wupeiqi/Downloads/test 下的文件打包放置當前程序目錄
    2  
    3 import shutil
    4 ret = shutil.make_archive("wwwwwwwwww", 'gztar', root_dir='/Users/wupeiqi/Downloads/test')
    5  
    6  
    7 #將 /Users/wupeiqi/Downloads/test 下的文件打包放置 /Users/wupeiqi/目錄
    8 import shutil
    9 ret = shutil.make_archive("/Users/wupeiqi/wwwwwwwwww", 'gztar', root_dir='/Users/wupeiqi/Downloads/test')
     1 def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0,
     2                  dry_run=0, owner=None, group=None, logger=None):
     3     """Create an archive file (eg. zip or tar).
     4 
     5     'base_name' is the name of the file to create, minus any format-specific
     6     extension; 'format' is the archive format: one of "zip", "tar", "bztar"
     7     or "gztar".
     8 
     9     'root_dir' is a directory that will be the root directory of the
    10     archive; ie. we typically chdir into 'root_dir' before creating the
    11     archive.  'base_dir' is the directory where we start archiving from;
    12     ie. 'base_dir' will be the common prefix of all files and
    13     directories in the archive.  'root_dir' and 'base_dir' both default
    14     to the current directory.  Returns the name of the archive file.
    15 
    16     'owner' and 'group' are used when creating a tar archive. By default,
    17     uses the current owner and group.
    18     """
    19     save_cwd = os.getcwd()
    20     if root_dir is not None:
    21         if logger is not None:
    22             logger.debug("changing into '%s'", root_dir)
    23         base_name = os.path.abspath(base_name)
    24         if not dry_run:
    25             os.chdir(root_dir)
    26 
    27     if base_dir is None:
    28         base_dir = os.curdir
    29 
    30     kwargs = {'dry_run': dry_run, 'logger': logger}
    31 
    32     try:
    33         format_info = _ARCHIVE_FORMATS[format]
    34     except KeyError:
    35         raise ValueError, "unknown archive format '%s'" % format
    36 
    37     func = format_info[0]
    38     for arg, val in format_info[1]:
    39         kwargs[arg] = val
    40 
    41     if format != 'zip':
    42         kwargs['owner'] = owner
    43         kwargs['group'] = group
    44 
    45     try:
    46         filename = func(base_name, base_dir, **kwargs)
    47     finally:
    48         if root_dir is not None:
    49             if logger is not None:
    50                 logger.debug("changing back to '%s'", save_cwd)
    51             os.chdir(save_cwd)
    52 
    53     return filename
    View Code

     

  • shutil 對壓縮包的處理是調用 ZipFile 和 TarFile 兩個模塊來進行的,詳細:

  •  1 import zipfile
     2 
     3 # 壓縮
     4 z = zipfile.ZipFile('laxi.zip', 'w')
     5 z.write('a.log')
     6 z.write('data.data')
     7 z.close()
     8 
     9 # 解壓
    10 z = zipfile.ZipFile('laxi.zip', 'r')
    11 z.extractall()
    12 z.close()
    zipfile 壓縮解壓
     1 import tarfile
     2 
     3 # 壓縮
     4 tar = tarfile.open('your.tar','w')
     5 tar.add('/Users/wupeiqi/PycharmProjects/bbs2.zip', arcname='bbs2.zip')
     6 tar.add('/Users/wupeiqi/PycharmProjects/cmdb.zip', arcname='cmdb.zip')
     7 tar.close()
     8 
     9 # 解壓
    10 tar = tarfile.open('your.tar','r')
    11 tar.extractall()  # 可設置解壓地址
    12 tar.close()
    tarfile 壓縮解壓
      1 class ZipFile(object):
      2     """ Class with methods to open, read, write, close, list zip files.
      3 
      4     z = ZipFile(file, mode="r", compression=ZIP_STORED, allowZip64=False)
      5 
      6     file: Either the path to the file, or a file-like object.
      7           If it is a path, the file will be opened and closed by ZipFile.
      8     mode: The mode can be either read "r", write "w" or append "a".
      9     compression: ZIP_STORED (no compression) or ZIP_DEFLATED (requires zlib).
     10     allowZip64: if True ZipFile will create files with ZIP64 extensions when
     11                 needed, otherwise it will raise an exception when this would
     12                 be necessary.
     13 
     14     """
     15 
     16     fp = None                   # Set here since __del__ checks it
     17 
     18     def __init__(self, file, mode="r", compression=ZIP_STORED, allowZip64=False):
     19         """Open the ZIP file with mode read "r", write "w" or append "a"."""
     20         if mode not in ("r", "w", "a"):
     21             raise RuntimeError('ZipFile() requires mode "r", "w", or "a"')
     22 
     23         if compression == ZIP_STORED:
     24             pass
     25         elif compression == ZIP_DEFLATED:
     26             if not zlib:
     27                 raise RuntimeError,\
     28                       "Compression requires the (missing) zlib module"
     29         else:
     30             raise RuntimeError, "That compression method is not supported"
     31 
     32         self._allowZip64 = allowZip64
     33         self._didModify = False
     34         self.debug = 0  # Level of printing: 0 through 3
     35         self.NameToInfo = {}    # Find file info given name
     36         self.filelist = []      # List of ZipInfo instances for archive
     37         self.compression = compression  # Method of compression
     38         self.mode = key = mode.replace('b', '')[0]
     39         self.pwd = None
     40         self._comment = ''
     41 
     42         # Check if we were passed a file-like object
     43         if isinstance(file, basestring):
     44             self._filePassed = 0
     45             self.filename = file
     46             modeDict = {'r' : 'rb', 'w': 'wb', 'a' : 'r+b'}
     47             try:
     48                 self.fp = open(file, modeDict[mode])
     49             except IOError:
     50                 if mode == 'a':
     51                     mode = key = 'w'
     52                     self.fp = open(file, modeDict[mode])
     53                 else:
     54                     raise
     55         else:
     56             self._filePassed = 1
     57             self.fp = file
     58             self.filename = getattr(file, 'name', None)
     59 
     60         try:
     61             if key == 'r':
     62                 self._RealGetContents()
     63             elif key == 'w':
     64                 # set the modified flag so central directory gets written
     65                 # even if no files are added to the archive
     66                 self._didModify = True
     67             elif key == 'a':
     68                 try:
     69                     # See if file is a zip file
     70                     self._RealGetContents()
     71                     # seek to start of directory and overwrite
     72                     self.fp.seek(self.start_dir, 0)
     73                 except BadZipfile:
     74                     # file is not a zip file, just append
     75                     self.fp.seek(0, 2)
     76 
     77                     # set the modified flag so central directory gets written
     78                     # even if no files are added to the archive
     79                     self._didModify = True
     80             else:
     81                 raise RuntimeError('Mode must be "r", "w" or "a"')
     82         except:
     83             fp = self.fp
     84             self.fp = None
     85             if not self._filePassed:
     86                 fp.close()
     87             raise
     88 
     89     def __enter__(self):
     90         return self
     91 
     92     def __exit__(self, type, value, traceback):
     93         self.close()
     94 
     95     def _RealGetContents(self):
     96         """Read in the table of contents for the ZIP file."""
     97         fp = self.fp
     98         try:
     99             endrec = _EndRecData(fp)
    100         except IOError:
    101             raise BadZipfile("File is not a zip file")
    102         if not endrec:
    103             raise BadZipfile, "File is not a zip file"
    104         if self.debug > 1:
    105             print endrec
    106         size_cd = endrec[_ECD_SIZE]             # bytes in central directory
    107         offset_cd = endrec[_ECD_OFFSET]         # offset of central directory
    108         self._comment = endrec[_ECD_COMMENT]    # archive comment
    109 
    110         # "concat" is zero, unless zip was concatenated to another file
    111         concat = endrec[_ECD_LOCATION] - size_cd - offset_cd
    112         if endrec[_ECD_SIGNATURE] == stringEndArchive64:
    113             # If Zip64 extension structures are present, account for them
    114             concat -= (sizeEndCentDir64 + sizeEndCentDir64Locator)
    115 
    116         if self.debug > 2:
    117             inferred = concat + offset_cd
    118             print "given, inferred, offset", offset_cd, inferred, concat
    119         # self.start_dir:  Position of start of central directory
    120         self.start_dir = offset_cd + concat
    121         fp.seek(self.start_dir, 0)
    122         data = fp.read(size_cd)
    123         fp = cStringIO.StringIO(data)
    124         total = 0
    125         while total < size_cd:
    126             centdir = fp.read(sizeCentralDir)
    127             if len(centdir) != sizeCentralDir:
    128                 raise BadZipfile("Truncated central directory")
    129             centdir = struct.unpack(structCentralDir, centdir)
    130             if centdir[_CD_SIGNATURE] != stringCentralDir:
    131                 raise BadZipfile("Bad magic number for central directory")
    132             if self.debug > 2:
    133                 print centdir
    134             filename = fp.read(centdir[_CD_FILENAME_LENGTH])
    135             # Create ZipInfo instance to store file information
    136             x = ZipInfo(filename)
    137             x.extra = fp.read(centdir[_CD_EXTRA_FIELD_LENGTH])
    138             x.comment = fp.read(centdir[_CD_COMMENT_LENGTH])
    139             x.header_offset = centdir[_CD_LOCAL_HEADER_OFFSET]
    140             (x.create_version, x.create_system, x.extract_version, x.reserved,
    141                 x.flag_bits, x.compress_type, t, d,
    142                 x.CRC, x.compress_size, x.file_size) = centdir[1:12]
    143             x.volume, x.internal_attr, x.external_attr = centdir[15:18]
    144             # Convert date/time code to (year, month, day, hour, min, sec)
    145             x._raw_time = t
    146             x.date_time = ( (d>>9)+1980, (d>>5)&0xF, d&0x1F,
    147                                      t>>11, (t>>5)&0x3F, (t&0x1F) * 2 )
    148 
    149             x._decodeExtra()
    150             x.header_offset = x.header_offset + concat
    151             x.filename = x._decodeFilename()
    152             self.filelist.append(x)
    153             self.NameToInfo[x.filename] = x
    154 
    155             # update total bytes read from central directory
    156             total = (total + sizeCentralDir + centdir[_CD_FILENAME_LENGTH]
    157                      + centdir[_CD_EXTRA_FIELD_LENGTH]
    158                      + centdir[_CD_COMMENT_LENGTH])
    159 
    160             if self.debug > 2:
    161                 print "total", total
    162 
    163 
    164     def namelist(self):
    165         """Return a list of file names in the archive."""
    166         l = []
    167         for data in self.filelist:
    168             l.append(data.filename)
    169         return l
    170 
    171     def infolist(self):
    172         """Return a list of class ZipInfo instances for files in the
    173         archive."""
    174         return self.filelist
    175 
    176     def printdir(self):
    177         """Print a table of contents for the zip file."""
    178         print "%-46s %19s %12s" % ("File Name", "Modified    ", "Size")
    179         for zinfo in self.filelist:
    180             date = "%d-%02d-%02d %02d:%02d:%02d" % zinfo.date_time[:6]
    181             print "%-46s %s %12d" % (zinfo.filename, date, zinfo.file_size)
    182 
    183     def testzip(self):
    184         """Read all the files and check the CRC."""
    185         chunk_size = 2 ** 20
    186         for zinfo in self.filelist:
    187             try:
    188                 # Read by chunks, to avoid an OverflowError or a
    189                 # MemoryError with very large embedded files.
    190                 with self.open(zinfo.filename, "r") as f:
    191                     while f.read(chunk_size):     # Check CRC-32
    192                         pass
    193             except BadZipfile:
    194                 return zinfo.filename
    195 
    196     def getinfo(self, name):
    197         """Return the instance of ZipInfo given 'name'."""
    198         info = self.NameToInfo.get(name)
    199         if info is None:
    200             raise KeyError(
    201                 'There is no item named %r in the archive' % name)
    202 
    203         return info
    204 
    205     def setpassword(self, pwd):
    206         """Set default password for encrypted files."""
    207         self.pwd = pwd
    208 
    209     @property
    210     def comment(self):
    211         """The comment text associated with the ZIP file."""
    212         return self._comment
    213 
    214     @comment.setter
    215     def comment(self, comment):
    216         # check for valid comment length
    217         if len(comment) > ZIP_MAX_COMMENT:
    218             import warnings
    219             warnings.warn('Archive comment is too long; truncating to %d bytes'
    220                           % ZIP_MAX_COMMENT, stacklevel=2)
    221             comment = comment[:ZIP_MAX_COMMENT]
    222         self._comment = comment
    223         self._didModify = True
    224 
    225     def read(self, name, pwd=None):
    226         """Return file bytes (as a string) for name."""
    227         return self.open(name, "r", pwd).read()
    228 
    229     def open(self, name, mode="r", pwd=None):
    230         """Return file-like object for 'name'."""
    231         if mode not in ("r", "U", "rU"):
    232             raise RuntimeError, 'open() requires mode "r", "U", or "rU"'
    233         if not self.fp:
    234             raise RuntimeError, \
    235                   "Attempt to read ZIP archive that was already closed"
    236 
    237         # Only open a new file for instances where we were not
    238         # given a file object in the constructor
    239         if self._filePassed:
    240             zef_file = self.fp
    241             should_close = False
    242         else:
    243             zef_file = open(self.filename, 'rb')
    244             should_close = True
    245 
    246         try:
    247             # Make sure we have an info object
    248             if isinstance(name, ZipInfo):
    249                 # 'name' is already an info object
    250                 zinfo = name
    251             else:
    252                 # Get info object for name
    253                 zinfo = self.getinfo(name)
    254 
    255             zef_file.seek(zinfo.header_offset, 0)
    256 
    257             # Skip the file header:
    258             fheader = zef_file.read(sizeFileHeader)
    259             if len(fheader) != sizeFileHeader:
    260                 raise BadZipfile("Truncated file header")
    261             fheader = struct.unpack(structFileHeader, fheader)
    262             if fheader[_FH_SIGNATURE] != stringFileHeader:
    263                 raise BadZipfile("Bad magic number for file header")
    264 
    265             fname = zef_file.read(fheader[_FH_FILENAME_LENGTH])
    266             if fheader[_FH_EXTRA_FIELD_LENGTH]:
    267                 zef_file.read(fheader[_FH_EXTRA_FIELD_LENGTH])
    268 
    269             if fname != zinfo.orig_filename:
    270                 raise BadZipfile, \
    271                         'File name in directory "%s" and header "%s" differ.' % (
    272                             zinfo.orig_filename, fname)
    273 
    274             # check for encrypted flag & handle password
    275             is_encrypted = zinfo.flag_bits & 0x1
    276             zd = None
    277             if is_encrypted:
    278                 if not pwd:
    279                     pwd = self.pwd
    280                 if not pwd:
    281                     raise RuntimeError, "File %s is encrypted, " \
    282                         "password required for extraction" % name
    283 
    284                 zd = _ZipDecrypter(pwd)
    285                 # The first 12 bytes in the cypher stream is an encryption header
    286                 #  used to strengthen the algorithm. The first 11 bytes are
    287                 #  completely random, while the 12th contains the MSB of the CRC,
    288                 #  or the MSB of the file time depending on the header type
    289                 #  and is used to check the correctness of the password.
    290                 bytes = zef_file.read(12)
    291                 h = map(zd, bytes[0:12])
    292                 if zinfo.flag_bits & 0x8:
    293                     # compare against the file type from extended local headers
    294                     check_byte = (zinfo._raw_time >> 8) & 0xff
    295                 else:
    296                     # compare against the CRC otherwise
    297                     check_byte = (zinfo.CRC >> 24) & 0xff
    298                 if ord(h[11]) != check_byte:
    299                     raise RuntimeError("Bad password for file", name)
    300 
    301             return ZipExtFile(zef_file, mode, zinfo, zd,
    302                     close_fileobj=should_close)
    303         except:
    304             if should_close:
    305                 zef_file.close()
    306             raise
    307 
    308     def extract(self, member, path=None, pwd=None):
    309         """Extract a member from the archive to the current working directory,
    310            using its full name. Its file information is extracted as accurately
    311            as possible. `member' may be a filename or a ZipInfo object. You can
    312            specify a different directory using `path'.
    313         """
    314         if not isinstance(member, ZipInfo):
    315             member = self.getinfo(member)
    316 
    317         if path is None:
    318             path = os.getcwd()
    319 
    320         return self._extract_member(member, path, pwd)
    321 
    322     def extractall(self, path=None, members=None, pwd=None):
    323         """Extract all members from the archive to the current working
    324            directory. `path' specifies a different directory to extract to.
    325            `members' is optional and must be a subset of the list returned
    326            by namelist().
    327         """
    328         if members is None:
    329             members = self.namelist()
    330 
    331         for zipinfo in members:
    332             self.extract(zipinfo, path, pwd)
    333 
    334     def _extract_member(self, member, targetpath, pwd):
    335         """Extract the ZipInfo object 'member' to a physical
    336            file on the path targetpath.
    337         """
    338         # build the destination pathname, replacing
    339         # forward slashes to platform specific separators.
    340         arcname = member.filename.replace('/', os.path.sep)
    341 
    342         if os.path.altsep:
    343             arcname = arcname.replace(os.path.altsep, os.path.sep)
    344         # interpret absolute pathname as relative, remove drive letter or
    345         # UNC path, redundant separators, "." and ".." components.
    346         arcname = os.path.splitdrive(arcname)[1]
    347         arcname = os.path.sep.join(x for x in arcname.split(os.path.sep)
    348                     if x not in ('', os.path.curdir, os.path.pardir))
    349         if os.path.sep == '\\':
    350             # filter illegal characters on Windows
    351             illegal = ':<>|"?*'
    352             if isinstance(arcname, unicode):
    353                 table = {ord(c): ord('_') for c in illegal}
    354             else:
    355                 table = string.maketrans(illegal, '_' * len(illegal))
    356             arcname = arcname.translate(table)
    357             # remove trailing dots
    358             arcname = (x.rstrip('.') for x in arcname.split(os.path.sep))
    359             arcname = os.path.sep.join(x for x in arcname if x)
    360 
    361         targetpath = os.path.join(targetpath, arcname)
    362         targetpath = os.path.normpath(targetpath)
    363 
    364         # Create all upper directories if necessary.
    365         upperdirs = os.path.dirname(targetpath)
    366         if upperdirs and not os.path.exists(upperdirs):
    367             os.makedirs(upperdirs)
    368 
    369         if member.filename[-1] == '/':
    370             if not os.path.isdir(targetpath):
    371                 os.mkdir(targetpath)
    372             return targetpath
    373 
    374         with self.open(member, pwd=pwd) as source, \
    375              file(targetpath, "wb") as target:
    376             shutil.copyfileobj(source, target)
    377 
    378         return targetpath
    379 
    380     def _writecheck(self, zinfo):
    381         """Check for errors before writing a file to the archive."""
    382         if zinfo.filename in self.NameToInfo:
    383             import warnings
    384             warnings.warn('Duplicate name: %r' % zinfo.filename, stacklevel=3)
    385         if self.mode not in ("w", "a"):
    386             raise RuntimeError, 'write() requires mode "w" or "a"'
    387         if not self.fp:
    388             raise RuntimeError, \
    389                   "Attempt to write ZIP archive that was already closed"
    390         if zinfo.compress_type == ZIP_DEFLATED and not zlib:
    391             raise RuntimeError, \
    392                   "Compression requires the (missing) zlib module"
    393         if zinfo.compress_type not in (ZIP_STORED, ZIP_DEFLATED):
    394             raise RuntimeError, \
    395                   "That compression method is not supported"
    396         if not self._allowZip64:
    397             requires_zip64 = None
    398             if len(self.filelist) >= ZIP_FILECOUNT_LIMIT:
    399                 requires_zip64 = "Files count"
    400             elif zinfo.file_size > ZIP64_LIMIT:
    401                 requires_zip64 = "Filesize"
    402             elif zinfo.header_offset > ZIP64_LIMIT:
    403                 requires_zip64 = "Zipfile size"
    404             if requires_zip64:
    405                 raise LargeZipFile(requires_zip64 +
    406                                    " would require ZIP64 extensions")
    407 
    408     def write(self, filename, arcname=None, compress_type=None):
    409         """Put the bytes from filename into the archive under the name
    410         arcname."""
    411         if not self.fp:
    412             raise RuntimeError(
    413                   "Attempt to write to ZIP archive that was already closed")
    414 
    415         st = os.stat(filename)
    416         isdir = stat.S_ISDIR(st.st_mode)
    417         mtime = time.localtime(st.st_mtime)
    418         date_time = mtime[0:6]
    419         # Create ZipInfo instance to store file information
    420         if arcname is None:
    421             arcname = filename
    422         arcname = os.path.normpath(os.path.splitdrive(arcname)[1])
    423         while arcname[0] in (os.sep, os.altsep):
    424             arcname = arcname[1:]
    425         if isdir:
    426             arcname += '/'
    427         zinfo = ZipInfo(arcname, date_time)
    428         zinfo.external_attr = (st[0] & 0xFFFF) << 16L      # Unix attributes
    429         if compress_type is None:
    430             zinfo.compress_type = self.compression
    431         else:
    432             zinfo.compress_type = compress_type
    433 
    434         zinfo.file_size = st.st_size
    435         zinfo.flag_bits = 0x00
    436         zinfo.header_offset = self.fp.tell()    # Start of header bytes
    437 
    438         self._writecheck(zinfo)
    439         self._didModify = True
    440 
    441         if isdir:
    442             zinfo.file_size = 0
    443             zinfo.compress_size = 0
    444             zinfo.CRC = 0
    445             zinfo.external_attr |= 0x10  # MS-DOS directory flag
    446             self.filelist.append(zinfo)
    447             self.NameToInfo[zinfo.filename] = zinfo
    448             self.fp.write(zinfo.FileHeader(False))
    449             return
    450 
    451         with open(filename, "rb") as fp:
    452             # Must overwrite CRC and sizes with correct data later
    453             zinfo.CRC = CRC = 0
    454             zinfo.compress_size = compress_size = 0
    455             # Compressed size can be larger than uncompressed size
    456             zip64 = self._allowZip64 and \
    457                     zinfo.file_size * 1.05 > ZIP64_LIMIT
    458             self.fp.write(zinfo.FileHeader(zip64))
    459             if zinfo.compress_type == ZIP_DEFLATED:
    460                 cmpr = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
    461                      zlib.DEFLATED, -15)
    462             else:
    463                 cmpr = None
    464             file_size = 0
    465             while 1:
    466                 buf = fp.read(1024 * 8)
    467                 if not buf:
    468                     break
    469                 file_size = file_size + len(buf)
    470                 CRC = crc32(buf, CRC) & 0xffffffff
    471                 if cmpr:
    472                     buf = cmpr.compress(buf)
    473                     compress_size = compress_size + len(buf)
    474                 self.fp.write(buf)
    475         if cmpr:
    476             buf = cmpr.flush()
    477             compress_size = compress_size + len(buf)
    478             self.fp.write(buf)
    479             zinfo.compress_size = compress_size
    480         else:
    481             zinfo.compress_size = file_size
    482         zinfo.CRC = CRC
    483         zinfo.file_size = file_size
    484         if not zip64 and self._allowZip64:
    485             if file_size > ZIP64_LIMIT:
    486                 raise RuntimeError('File size has increased during compressing')
    487             if compress_size > ZIP64_LIMIT:
    488                 raise RuntimeError('Compressed size larger than uncompressed size')
    489         # Seek backwards and write file header (which will now include
    490         # correct CRC and file sizes)
    491         position = self.fp.tell()       # Preserve current position in file
    492         self.fp.seek(zinfo.header_offset, 0)
    493         self.fp.write(zinfo.FileHeader(zip64))
    494         self.fp.seek(position, 0)
    495         self.filelist.append(zinfo)
    496         self.NameToInfo[zinfo.filename] = zinfo
    497 
    498     def writestr(self, zinfo_or_arcname, bytes, compress_type=None):
    499         """Write a file into the archive.  The contents is the string
    500         'bytes'.  'zinfo_or_arcname' is either a ZipInfo instance or
    501         the name of the file in the archive."""
    502         if not isinstance(zinfo_or_arcname, ZipInfo):
    503             zinfo = ZipInfo(filename=zinfo_or_arcname,
    504                             date_time=time.localtime(time.time())[:6])
    505 
    506             zinfo.compress_type = self.compression
    507             if zinfo.filename[-1] == '/':
    508                 zinfo.external_attr = 0o40775 << 16   # drwxrwxr-x
    509                 zinfo.external_attr |= 0x10           # MS-DOS directory flag
    510             else:
    511                 zinfo.external_attr = 0o600 << 16     # ?rw-------
    512         else:
    513             zinfo = zinfo_or_arcname
    514 
    515         if not self.fp:
    516             raise RuntimeError(
    517                   "Attempt to write to ZIP archive that was already closed")
    518 
    519         if compress_type is not None:
    520             zinfo.compress_type = compress_type
    521 
    522         zinfo.file_size = len(bytes)            # Uncompressed size
    523         zinfo.header_offset = self.fp.tell()    # Start of header bytes
    524         self._writecheck(zinfo)
    525         self._didModify = True
    526         zinfo.CRC = crc32(bytes) & 0xffffffff       # CRC-32 checksum
    527         if zinfo.compress_type == ZIP_DEFLATED:
    528             co = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
    529                  zlib.DEFLATED, -15)
    530             bytes = co.compress(bytes) + co.flush()
    531             zinfo.compress_size = len(bytes)    # Compressed size
    532         else:
    533             zinfo.compress_size = zinfo.file_size
    534         zip64 = zinfo.file_size > ZIP64_LIMIT or \
    535                 zinfo.compress_size > ZIP64_LIMIT
    536         if zip64 and not self._allowZip64:
    537             raise LargeZipFile("Filesize would require ZIP64 extensions")
    538         self.fp.write(zinfo.FileHeader(zip64))
    539         self.fp.write(bytes)
    540         if zinfo.flag_bits & 0x08:
    541             # Write CRC and file sizes after the file data
    542             fmt = '<LQQ' if zip64 else '<LLL'
    543             self.fp.write(struct.pack(fmt, zinfo.CRC, zinfo.compress_size,
    544                   zinfo.file_size))
    545         self.fp.flush()
    546         self.filelist.append(zinfo)
    547         self.NameToInfo[zinfo.filename] = zinfo
    548 
    549     def __del__(self):
    550         """Call the "close()" method in case the user forgot."""
    551         self.close()
    552 
    553     def close(self):
    554         """Close the file, and for mode "w" and "a" write the ending
    555         records."""
    556         if self.fp is None:
    557             return
    558 
    559         try:
    560             if self.mode in ("w", "a") and self._didModify: # write ending records
    561                 pos1 = self.fp.tell()
    562                 for zinfo in self.filelist:         # write central directory
    563                     dt = zinfo.date_time
    564                     dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2]
    565                     dostime = dt[3] << 11 | dt[4] << 5 | (dt[5] // 2)
    566                     extra = []
    567                     if zinfo.file_size > ZIP64_LIMIT \
    568                             or zinfo.compress_size > ZIP64_LIMIT:
    569                         extra.append(zinfo.file_size)
    570                         extra.append(zinfo.compress_size)
    571                         file_size = 0xffffffff
    572                         compress_size = 0xffffffff
    573                     else:
    574                         file_size = zinfo.file_size
    575                         compress_size = zinfo.compress_size
    576 
    577                     if zinfo.header_offset > ZIP64_LIMIT:
    578                         extra.append(zinfo.header_offset)
    579                         header_offset = 0xffffffffL
    580                     else:
    581                         header_offset = zinfo.header_offset
    582 
    583                     extra_data = zinfo.extra
    584                     if extra:
    585                         # Append a ZIP64 field to the extra's
    586                         extra_data = struct.pack(
    587                                 '<HH' + 'Q'*len(extra),
    588                                 1, 8*len(extra), *extra) + extra_data
    589 
    590                         extract_version = max(45, zinfo.extract_version)
    591                         create_version = max(45, zinfo.create_version)
    592                     else:
    593                         extract_version = zinfo.extract_version
    594                         create_version = zinfo.create_version
    595 
    596                     try:
    597                         filename, flag_bits = zinfo._encodeFilenameFlags()
    598                         centdir = struct.pack(structCentralDir,
    599                         stringCentralDir, create_version,
    600                         zinfo.create_system, extract_version, zinfo.reserved,
    601                         flag_bits, zinfo.compress_type, dostime, dosdate,
    602                         zinfo.CRC, compress_size, file_size,
    603                         len(filename), len(extra_data), len(zinfo.comment),
    604                         0, zinfo.internal_attr, zinfo.external_attr,
    605                         header_offset)
    606                     except DeprecationWarning:
    607                         print >>sys.stderr, (structCentralDir,
    608                         stringCentralDir, create_version,
    609                         zinfo.create_system, extract_version, zinfo.reserved,
    610                         zinfo.flag_bits, zinfo.compress_type, dostime, dosdate,
    611                         zinfo.CRC, compress_size, file_size,
    612                         len(zinfo.filename), len(extra_data), len(zinfo.comment),
    613                         0, zinfo.internal_attr, zinfo.external_attr,
    614                         header_offset)
    615                         raise
    616                     self.fp.write(centdir)
    617                     self.fp.write(filename)
    618                     self.fp.write(extra_data)
    619                     self.fp.write(zinfo.comment)
    620 
    621                 pos2 = self.fp.tell()
    622                 # Write end-of-zip-archive record
    623                 centDirCount = len(self.filelist)
    624                 centDirSize = pos2 - pos1
    625                 centDirOffset = pos1
    626                 requires_zip64 = None
    627                 if centDirCount > ZIP_FILECOUNT_LIMIT:
    628                     requires_zip64 = "Files count"
    629                 elif centDirOffset > ZIP64_LIMIT:
    630                     requires_zip64 = "Central directory offset"
    631                 elif centDirSize > ZIP64_LIMIT:
    632                     requires_zip64 = "Central directory size"
    633                 if requires_zip64:
    634                     # Need to write the ZIP64 end-of-archive records
    635                     if not self._allowZip64:
    636                         raise LargeZipFile(requires_zip64 +
    637                                            " would require ZIP64 extensions")
    638                     zip64endrec = struct.pack(
    639                             structEndArchive64, stringEndArchive64,
    640                             44, 45, 45, 0, 0, centDirCount, centDirCount,
    641                             centDirSize, centDirOffset)
    642                     self.fp.write(zip64endrec)
    643 
    644                     zip64locrec = struct.pack(
    645                             structEndArchive64Locator,
    646                             stringEndArchive64Locator, 0, pos2, 1)
    647                     self.fp.write(zip64locrec)
    648                     centDirCount = min(centDirCount, 0xFFFF)
    649                     centDirSize = min(centDirSize, 0xFFFFFFFF)
    650                     centDirOffset = min(centDirOffset, 0xFFFFFFFF)
    651 
    652                 endrec = struct.pack(structEndArchive, stringEndArchive,
    653                                     0, 0, centDirCount, centDirCount,
    654                                     centDirSize, centDirOffset, len(self._comment))
    655                 self.fp.write(endrec)
    656                 self.fp.write(self._comment)
    657                 self.fp.flush()
    658         finally:
    659             fp = self.fp
    660             self.fp = None
    661             if not self._filePassed:
    662                 fp.close()
    ZipFile源代碼
      1 class TarFile(object):
      2     """The TarFile Class provides an interface to tar archives.
      3     """
      4 
      5     debug = 0                   # May be set from 0 (no msgs) to 3 (all msgs)
      6 
      7     dereference = False         # If true, add content of linked file to the
      8                                 # tar file, else the link.
      9 
     10     ignore_zeros = False        # If true, skips empty or invalid blocks and
     11                                 # continues processing.
     12 
     13     errorlevel = 1              # If 0, fatal errors only appear in debug
     14                                 # messages (if debug >= 0). If > 0, errors
     15                                 # are passed to the caller as exceptions.
     16 
     17     format = DEFAULT_FORMAT     # The format to use when creating an archive.
     18 
     19     encoding = ENCODING         # Encoding for 8-bit character strings.
     20 
     21     errors = None               # Error handler for unicode conversion.
     22 
     23     tarinfo = TarInfo           # The default TarInfo class to use.
     24 
     25     fileobject = ExFileObject   # The default ExFileObject class to use.
     26 
     27     def __init__(self, name=None, mode="r", fileobj=None, format=None,
     28             tarinfo=None, dereference=None, ignore_zeros=None, encoding=None,
     29             errors=None, pax_headers=None, debug=None, errorlevel=None):
     30         """Open an (uncompressed) tar archive `name'. `mode' is either 'r' to
     31            read from an existing archive, 'a' to append data to an existing
     32            file or 'w' to create a new file overwriting an existing one. `mode'
     33            defaults to 'r'.
     34            If `fileobj' is given, it is used for reading or writing data. If it
     35            can be determined, `mode' is overridden by `fileobj's mode.
     36            `fileobj' is not closed, when TarFile is closed.
     37         """
     38         modes = {"r": "rb", "a": "r+b", "w": "wb"}
     39         if mode not in modes:
     40             raise ValueError("mode must be 'r', 'a' or 'w'")
     41         self.mode = mode
     42         self._mode = modes[mode]
     43 
     44         if not fileobj:
     45             if self.mode == "a" and not os.path.exists(name):
     46                 # Create nonexistent files in append mode.
     47                 self.mode = "w"
     48                 self._mode = "wb"
     49             fileobj = bltn_open(name, self._mode)
     50             self._extfileobj = False
     51         else:
     52             if name is None and hasattr(fileobj, "name"):
     53                 name = fileobj.name
     54             if hasattr(fileobj, "mode"):
     55                 self._mode = fileobj.mode
     56             self._extfileobj = True
     57         self.name = os.path.abspath(name) if name else None
     58         self.fileobj = fileobj
     59 
     60         # Init attributes.
     61         if format is not None:
     62             self.format = format
     63         if tarinfo is not None:
     64             self.tarinfo = tarinfo
     65         if dereference is not None:
     66             self.dereference = dereference
     67         if ignore_zeros is not None:
     68             self.ignore_zeros = ignore_zeros
     69         if encoding is not None:
     70             self.encoding = encoding
     71 
     72         if errors is not None:
     73             self.errors = errors
     74         elif mode == "r":
     75             self.errors = "utf-8"
     76         else:
     77             self.errors = "strict"
     78 
     79         if pax_headers is not None and self.format == PAX_FORMAT:
     80             self.pax_headers = pax_headers
     81         else:
     82             self.pax_headers = {}
     83 
     84         if debug is not None:
     85             self.debug = debug
     86         if errorlevel is not None:
     87             self.errorlevel = errorlevel
     88 
     89         # Init datastructures.
     90         self.closed = False
     91         self.members = []       # list of members as TarInfo objects
     92         self._loaded = False    # flag if all members have been read
     93         self.offset = self.fileobj.tell()
     94                                 # current position in the archive file
     95         self.inodes = {}        # dictionary caching the inodes of
     96                                 # archive members already added
     97 
     98         try:
     99             if self.mode == "r":
    100                 self.firstmember = None
    101                 self.firstmember = self.next()
    102 
    103             if self.mode == "a":
    104                 # Move to the end of the archive,
    105                 # before the first empty block.
    106                 while True:
    107                     self.fileobj.seek(self.offset)
    108                     try:
    109                         tarinfo = self.tarinfo.fromtarfile(self)
    110                         self.members.append(tarinfo)
    111                     except EOFHeaderError:
    112                         self.fileobj.seek(self.offset)
    113                         break
    114                     except HeaderError, e:
    115                         raise ReadError(str(e))
    116 
    117             if self.mode in "aw":
    118                 self._loaded = True
    119 
    120                 if self.pax_headers:
    121                     buf = self.tarinfo.create_pax_global_header(self.pax_headers.copy())
    122                     self.fileobj.write(buf)
    123                     self.offset += len(buf)
    124         except:
    125             if not self._extfileobj:
    126                 self.fileobj.close()
    127             self.closed = True
    128             raise
    129 
    130     def _getposix(self):
    131         return self.format == USTAR_FORMAT
    132     def _setposix(self, value):
    133         import warnings
    134         warnings.warn("use the format attribute instead", DeprecationWarning,
    135                       2)
    136         if value:
    137             self.format = USTAR_FORMAT
    138         else:
    139             self.format = GNU_FORMAT
    140     posix = property(_getposix, _setposix)
    141 
    142     #--------------------------------------------------------------------------
    143     # Below are the classmethods which act as alternate constructors to the
    144     # TarFile class. The open() method is the only one that is needed for
    145     # public use; it is the "super"-constructor and is able to select an
    146     # adequate "sub"-constructor for a particular compression using the mapping
    147     # from OPEN_METH.
    148     #
    149     # This concept allows one to subclass TarFile without losing the comfort of
    150     # the super-constructor. A sub-constructor is registered and made available
    151     # by adding it to the mapping in OPEN_METH.
    152 
    153     @classmethod
    154     def open(cls, name=None, mode="r", fileobj=None, bufsize=RECORDSIZE, **kwargs):
    155         """Open a tar archive for reading, writing or appending. Return
    156            an appropriate TarFile class.
    157 
    158            mode:
    159            'r' or 'r:*' open for reading with transparent compression
    160            'r:'         open for reading exclusively uncompressed
    161            'r:gz'       open for reading with gzip compression
    162            'r:bz2'      open for reading with bzip2 compression
    163            'a' or 'a:'  open for appending, creating the file if necessary
    164            'w' or 'w:'  open for writing without compression
    165            'w:gz'       open for writing with gzip compression
    166            'w:bz2'      open for writing with bzip2 compression
    167 
    168            'r|*'        open a stream of tar blocks with transparent compression
    169            'r|'         open an uncompressed stream of tar blocks for reading
    170            'r|gz'       open a gzip compressed stream of tar blocks
    171            'r|bz2'      open a bzip2 compressed stream of tar blocks
    172            'w|'         open an uncompressed stream for writing
    173            'w|gz'       open a gzip compressed stream for writing
    174            'w|bz2'      open a bzip2 compressed stream for writing
    175         """
    176 
    177         if not name and not fileobj:
    178             raise ValueError("nothing to open")
    179 
    180         if mode in ("r", "r:*"):
    181             # Find out which *open() is appropriate for opening the file.
    182             for comptype in cls.OPEN_METH:
    183                 func = getattr(cls, cls.OPEN_METH[comptype])
    184                 if fileobj is not None:
    185                     saved_pos = fileobj.tell()
    186                 try:
    187                     return func(name, "r", fileobj, **kwargs)
    188                 except (ReadError, CompressionError), e:
    189                     if fileobj is not None:
    190                         fileobj.seek(saved_pos)
    191                     continue
    192             raise ReadError("file could not be opened successfully")
    193 
    194         elif ":" in mode:
    195             filemode, comptype = mode.split(":", 1)
    196             filemode = filemode or "r"
    197             comptype = comptype or "tar"
    198 
    199             # Select the *open() function according to
    200             # given compression.
    201             if comptype in cls.OPEN_METH:
    202                 func = getattr(cls, cls.OPEN_METH[comptype])
    203             else:
    204                 raise CompressionError("unknown compression type %r" % comptype)
    205             return func(name, filemode, fileobj, **kwargs)
    206 
    207         elif "|" in mode:
    208             filemode, comptype = mode.split("|", 1)
    209             filemode = filemode or "r"
    210             comptype = comptype or "tar"
    211 
    212             if filemode not in ("r", "w"):
    213                 raise ValueError("mode must be 'r' or 'w'")
    214 
    215             stream = _Stream(name, filemode, comptype, fileobj, bufsize)
    216             try:
    217                 t = cls(name, filemode, stream, **kwargs)
    218             except:
    219                 stream.close()
    220                 raise
    221             t._extfileobj = False
    222             return t
    223 
    224         elif mode in ("a", "w"):
    225             return cls.taropen(name, mode, fileobj, **kwargs)
    226 
    227         raise ValueError("undiscernible mode")
    228 
    229     @classmethod
    230     def taropen(cls, name, mode="r", fileobj=None, **kwargs):
    231         """Open uncompressed tar archive name for reading or writing.
    232         """
    233         if mode not in ("r", "a", "w"):
    234             raise ValueError("mode must be 'r', 'a' or 'w'")
    235         return cls(name, mode, fileobj, **kwargs)
    236 
    237     @classmethod
    238     def gzopen(cls, name, mode="r", fileobj=None, compresslevel=9, **kwargs):
    239         """Open gzip compressed tar archive name for reading or writing.
    240            Appending is not allowed.
    241         """
    242         if mode not in ("r", "w"):
    243             raise ValueError("mode must be 'r' or 'w'")
    244 
    245         try:
    246             import gzip
    247             gzip.GzipFile
    248         except (ImportError, AttributeError):
    249             raise CompressionError("gzip module is not available")
    250 
    251         try:
    252             fileobj = gzip.GzipFile(name, mode, compresslevel, fileobj)
    253         except OSError:
    254             if fileobj is not None and mode == 'r':
    255                 raise ReadError("not a gzip file")
    256             raise
    257 
    258         try:
    259             t = cls.taropen(name, mode, fileobj, **kwargs)
    260         except IOError:
    261             fileobj.close()
    262             if mode == 'r':
    263                 raise ReadError("not a gzip file")
    264             raise
    265         except:
    266             fileobj.close()
    267             raise
    268         t._extfileobj = False
    269         return t
    270 
    271     @classmethod
    272     def bz2open(cls, name, mode="r", fileobj=None, compresslevel=9, **kwargs):
    273         """Open bzip2 compressed tar archive name for reading or writing.
    274            Appending is not allowed.
    275         """
    276         if mode not in ("r", "w"):
    277             raise ValueError("mode must be 'r' or 'w'.")
    278 
    279         try:
    280             import bz2
    281         except ImportError:
    282             raise CompressionError("bz2 module is not available")
    283 
    284         if fileobj is not None:
    285             fileobj = _BZ2Proxy(fileobj, mode)
    286         else:
    287             fileobj = bz2.BZ2File(name, mode, compresslevel=compresslevel)
    288 
    289         try:
    290             t = cls.taropen(name, mode, fileobj, **kwargs)
    291         except (IOError, EOFError):
    292             fileobj.close()
    293             if mode == 'r':
    294                 raise ReadError("not a bzip2 file")
    295             raise
    296         except:
    297             fileobj.close()
    298             raise
    299         t._extfileobj = False
    300         return t
    301 
    302     # All *open() methods are registered here.
    303     OPEN_METH = {
    304         "tar": "taropen",   # uncompressed tar
    305         "gz":  "gzopen",    # gzip compressed tar
    306         "bz2": "bz2open"    # bzip2 compressed tar
    307     }
    308 
    309     #--------------------------------------------------------------------------
    310     # The public methods which TarFile provides:
    311 
    312     def close(self):
    313         """Close the TarFile. In write-mode, two finishing zero blocks are
    314            appended to the archive.
    315         """
    316         if self.closed:
    317             return
    318 
    319         if self.mode in "aw":
    320             self.fileobj.write(NUL * (BLOCKSIZE * 2))
    321             self.offset += (BLOCKSIZE * 2)
    322             # fill up the end with zero-blocks
    323             # (like option -b20 for tar does)
    324             blocks, remainder = divmod(self.offset, RECORDSIZE)
    325             if remainder > 0:
    326                 self.fileobj.write(NUL * (RECORDSIZE - remainder))
    327 
    328         if not self._extfileobj:
    329             self.fileobj.close()
    330         self.closed = True
    331 
    332     def getmember(self, name):
    333         """Return a TarInfo object for member `name'. If `name' can not be
    334            found in the archive, KeyError is raised. If a member occurs more
    335            than once in the archive, its last occurrence is assumed to be the
    336            most up-to-date version.
    337         """
    338         tarinfo = self._getmember(name)
    339         if tarinfo is None:
    340             raise KeyError("filename %r not found" % name)
    341         return tarinfo
    342 
    343     def getmembers(self):
    344         """Return the members of the archive as a list of TarInfo objects. The
    345            list has the same order as the members in the archive.
    346         """
    347         self._check()
    348         if not self._loaded:    # if we want to obtain a list of
    349             self._load()        # all members, we first have to
    350                                 # scan the whole archive.
    351         return self.members
    352 
    353     def getnames(self):
    354         """Return the members of the archive as a list of their names. It has
    355            the same order as the list returned by getmembers().
    356         """
    357         return [tarinfo.name for tarinfo in self.getmembers()]
    358 
    359     def gettarinfo(self, name=None, arcname=None, fileobj=None):
    360         """Create a TarInfo object for either the file `name' or the file
    361            object `fileobj' (using os.fstat on its file descriptor). You can
    362            modify some of the TarInfo's attributes before you add it using
    363            addfile(). If given, `arcname' specifies an alternative name for the
    364            file in the archive.
    365         """
    366         self._check("aw")
    367 
    368         # When fileobj is given, replace name by
    369         # fileobj's real name.
    370         if fileobj is not None:
    371             name = fileobj.name
    372 
    373         # Building the name of the member in the archive.
    374         # Backward slashes are converted to forward slashes,
    375         # Absolute paths are turned to relative paths.
    376         if arcname is None:
    377             arcname = name
    378         drv, arcname = os.path.splitdrive(arcname)
    379         arcname = arcname.replace(os.sep, "/")
    380         arcname = arcname.lstrip("/")
    381 
    382         # Now, fill the TarInfo object with
    383         # information specific for the file.
    384         tarinfo = self.tarinfo()
    385         tarinfo.tarfile = self
    386 
    387         # Use os.stat or os.lstat, depending on platform
    388         # and if symlinks shall be resolved.
    389         if fileobj is None:
    390             if hasattr(os, "lstat") and not self.dereference:
    391                 statres = os.lstat(name)
    392             else:
    393                 statres = os.stat(name)
    394         else:
    395             statres = os.fstat(fileobj.fileno())
    396         linkname = ""
    397 
    398         stmd = statres.st_mode
    399         if stat.S_ISREG(stmd):
    400             inode = (statres.st_ino, statres.st_dev)
    401             if not self.dereference and statres.st_nlink > 1 and \
    402                     inode in self.inodes and arcname != self.inodes[inode]:
    403                 # Is it a hardlink to an already
    404                 # archived file?
    405                 type = LNKTYPE
    406                 linkname = self.inodes[inode]
    407             else:
    408                 # The inode is added only if its valid.
    409                 # For win32 it is always 0.
    410                 type = REGTYPE
    411                 if inode[0]:
    412                     self.inodes[inode] = arcname
    413         elif stat.S_ISDIR(stmd):
    414             type = DIRTYPE
    415         elif stat.S_ISFIFO(stmd):
    416             type = FIFOTYPE
    417         elif stat.S_ISLNK(stmd):
    418             type = SYMTYPE
    419             linkname = os.readlink(name)
    420         elif stat.S_ISCHR(stmd):
    421             type = CHRTYPE
    422         elif stat.S_ISBLK(stmd):
    423             type = BLKTYPE
    424         else:
    425             return None
    426 
    427         # Fill the TarInfo object with all
    428         # information we can get.
    429         tarinfo.name = arcname
    430         tarinfo.mode = stmd
    431         tarinfo.uid = statres.st_uid
    432         tarinfo.gid = statres.st_gid
    433         if type == REGTYPE:
    434             tarinfo.size = statres.st_size
    435         else:
    436             tarinfo.size = 0L
    437         tarinfo.mtime = statres.st_mtime
    438         tarinfo.type = type
    439         tarinfo.linkname = linkname
    440         if pwd:
    441             try:
    442                 tarinfo.uname = pwd.getpwuid(tarinfo.uid)[0]
    443             except KeyError:
    444                 pass
    445         if grp:
    446             try:
    447                 tarinfo.gname = grp.getgrgid(tarinfo.gid)[0]
    448             except KeyError:
    449                 pass
    450 
    451         if type in (CHRTYPE, BLKTYPE):
    452             if hasattr(os, "major") and hasattr(os, "minor"):
    453                 tarinfo.devmajor = os.major(statres.st_rdev)
    454                 tarinfo.devminor = os.minor(statres.st_rdev)
    455         return tarinfo
    456 
    457     def list(self, verbose=True):
    458         """Print a table of contents to sys.stdout. If `verbose' is False, only
    459            the names of the members are printed. If it is True, an `ls -l'-like
    460            output is produced.
    461         """
    462         self._check()
    463 
    464         for tarinfo in self:
    465             if verbose:
    466                 print filemode(tarinfo.mode),
    467                 print "%s/%s" % (tarinfo.uname or tarinfo.uid,
    468                                  tarinfo.gname or tarinfo.gid),
    469                 if tarinfo.ischr() or tarinfo.isblk():
    470                     print "%10s" % ("%d,%d" \
    471                                     % (tarinfo.devmajor, tarinfo.devminor)),
    472                 else:
    473                     print "%10d" % tarinfo.size,
    474                 print "%d-%02d-%02d %02d:%02d:%02d" \
    475                       % time.localtime(tarinfo.mtime)[:6],
    476 
    477             print tarinfo.name + ("/" if tarinfo.isdir() else ""),
    478 
    479             if verbose:
    480                 if tarinfo.issym():
    481                     print "->", tarinfo.linkname,
    482                 if tarinfo.islnk():
    483                     print "link to", tarinfo.linkname,
    484             print
    485 
    486     def add(self, name, arcname=None, recursive=True, exclude=None, filter=None):
    487         """Add the file `name' to the archive. `name' may be any type of file
    488            (directory, fifo, symbolic link, etc.). If given, `arcname'
    489            specifies an alternative name for the file in the archive.
    490            Directories are added recursively by default. This can be avoided by
    491            setting `recursive' to False. `exclude' is a function that should
    492            return True for each filename to be excluded. `filter' is a function
    493            that expects a TarInfo object argument and returns the changed
    494            TarInfo object, if it returns None the TarInfo object will be
    495            excluded from the archive.
    496         """
    497         self._check("aw")
    498 
    499         if arcname is None:
    500             arcname = name
    501 
    502         # Exclude pathnames.
    503         if exclude is not None:
    504             import warnings
    505             warnings.warn("use the filter argument instead",
    506                     DeprecationWarning, 2)
    507             if exclude(name):
    508                 self._dbg(2, "tarfile: Excluded %r" % name)
    509                 return
    510 
    511         # Skip if somebody tries to archive the archive...
    512         if self.name is not None and os.path.abspath(name) == self.name:
    513             self._dbg(2, "tarfile: Skipped %r" % name)
    514             return
    515 
    516         self._dbg(1, name)
    517 
    518         # Create a TarInfo object from the file.
    519         tarinfo = self.gettarinfo(name, arcname)
    520 
    521         if tarinfo is None:
    522             self._dbg(1, "tarfile: Unsupported type %r" % name)
    523             return
    524 
    525         # Change or exclude the TarInfo object.
    526         if filter is not None:
    527             tarinfo = filter(tarinfo)
    528             if tarinfo is None:
    529                 self._dbg(2, "tarfile: Excluded %r" % name)
    530                 return
    531 
    532         # Append the tar header and data to the archive.
    533         if tarinfo.isreg():
    534             with bltn_open(name, "rb") as f:
    535                 self.addfile(tarinfo, f)
    536 
    537         elif tarinfo.isdir():
    538             self.addfile(tarinfo)
    539             if recursive:
    540                 for f in os.listdir(name):
    541                     self.add(os.path.join(name, f), os.path.join(arcname, f),
    542                             recursive, exclude, filter)
    543 
    544         else:
    545             self.addfile(tarinfo)
    546 
    547     def addfile(self, tarinfo, fileobj=None):
    548         """Add the TarInfo object `tarinfo' to the archive. If `fileobj' is
    549            given, tarinfo.size bytes are read from it and added to the archive.
    550            You can create TarInfo objects using gettarinfo().
    551            On Windows platforms, `fileobj' should always be opened with mode
    552            'rb' to avoid irritation about the file size.
    553         """
    554         self._check("aw")
    555 
    556         tarinfo = copy.copy(tarinfo)
    557 
    558         buf = tarinfo.tobuf(self.format, self.encoding, self.errors)
    559         self.fileobj.write(buf)
    560         self.offset += len(buf)
    561 
    562         # If there's data to follow, append it.
    563         if fileobj is not None:
    564             copyfileobj(fileobj, self.fileobj, tarinfo.size)
    565             blocks, remainder = divmod(tarinfo.size, BLOCKSIZE)
    566             if remainder > 0:
    567                 self.fileobj.write(NUL * (BLOCKSIZE - remainder))
    568                 blocks += 1
    569             self.offset += blocks * BLOCKSIZE
    570 
    571         self.members.append(tarinfo)
    572 
    573     def extractall(self, path=".", members=None):
    574         """Extract all members from the archive to the current working
    575            directory and set owner, modification time and permissions on
    576            directories afterwards. `path' specifies a different directory
    577            to extract to. `members' is optional and must be a subset of the
    578            list returned by getmembers().
    579         """
    580         directories = []
    581 
    582         if members is None:
    583             members = self
    584 
    585         for tarinfo in members:
    586             if tarinfo.isdir():
    587                 # Extract directories with a safe mode.
    588                 directories.append(tarinfo)
    589                 tarinfo = copy.copy(tarinfo)
    590                 tarinfo.mode = 0700
    591             self.extract(tarinfo, path)
    592 
    593         # Reverse sort directories.
    594         directories.sort(key=operator.attrgetter('name'))
    595         directories.reverse()
    596 
    597         # Set correct owner, mtime and filemode on directories.
    598         for tarinfo in directories:
    599             dirpath = os.path.join(path, tarinfo.name)
    600             try:
    601                 self.chown(tarinfo, dirpath)
    602                 self.utime(tarinfo, dirpath)
    603                 self.chmod(tarinfo, dirpath)
    604             except ExtractError, e:
    605                 if self.errorlevel > 1:
    606                     raise
    607                 else:
    608                     self._dbg(1, "tarfile: %s" % e)
    609 
    610     def extract(self, member, path=""):
    611         """Extract a member from the archive to the current working directory,
    612            using its full name. Its file information is extracted as accurately
    613            as possible. `member' may be a filename or a TarInfo object. You can
    614            specify a different directory using `path'.
    615         """
    616         self._check("r")
    617 
    618         if isinstance(member, basestring):
    619             tarinfo = self.getmember(member)
    620         else:
    621             tarinfo = member
    622 
    623         # Prepare the link target for makelink().
    624         if tarinfo.islnk():
    625             tarinfo._link_target = os.path.join(path, tarinfo.linkname)
    626 
    627         try:
    628             self._extract_member(tarinfo, os.path.join(path, tarinfo.name))
    629         except EnvironmentError, e:
    630             if self.errorlevel > 0:
    631                 raise
    632             else:
    633                 if e.filename is None:
    634                     self._dbg(1, "tarfile: %s" % e.strerror)
    635                 else:
    636                     self._dbg(1, "tarfile: %s %r" % (e.strerror, e.filename))
    637         except ExtractError, e:
    638             if self.errorlevel > 1:
    639                 raise
    640             else:
    641                 self._dbg(1, "tarfile: %s" % e)
    642 
    643     def extractfile(self, member):
    644         """Extract a member from the archive as a file object. `member' may be
    645            a filename or a TarInfo object. If `member' is a regular file, a
    646            file-like object is returned. If `member' is a link, a file-like
    647            object is constructed from the link's target. If `member' is none of
    648            the above, None is returned.
    649            The file-like object is read-only and provides the following
    650            methods: read(), readline(), readlines(), seek() and tell()
    651         """
    652         self._check("r")
    653 
    654         if isinstance(member, basestring):
    655             tarinfo = self.getmember(member)
    656         else:
    657             tarinfo = member
    658 
    659         if tarinfo.isreg():
    660             return self.fileobject(self, tarinfo)
    661 
    662         elif tarinfo.type not in SUPPORTED_TYPES:
    663             # If a member's type is unknown, it is treated as a
    664             # regular file.
    665             return self.fileobject(self, tarinfo)
    666 
    667         elif tarinfo.islnk() or tarinfo.issym():
    668             if isinstance(self.fileobj, _Stream):
    669                 # A small but ugly workaround for the case that someone tries
    670                 # to extract a (sym)link as a file-object from a non-seekable
    671                 # stream of tar blocks.
    672                 raise StreamError("cannot extract (sym)link as file object")
    673             else:
    674                 # A (sym)link's file object is its target's file object.
    675                 return self.extractfile(self._find_link_target(tarinfo))
    676         else:
    677             # If there's no data associated with the member (directory, chrdev,
    678             # blkdev, etc.), return None instead of a file object.
    679             return None
    680 
    681     def _extract_member(self, tarinfo, targetpath):
    682         """Extract the TarInfo object tarinfo to a physical
    683            file called targetpath.
    684         """
    685         # Fetch the TarInfo object for the given name
    686         # and build the destination pathname, replacing
    687         # forward slashes to platform specific separators.
    688         targetpath = targetpath.rstrip("/")
    689         targetpath = targetpath.replace("/", os.sep)
    690 
    691         # Create all upper directories.
    692         upperdirs = os.path.dirname(targetpath)
    693         if upperdirs and not os.path.exists(upperdirs):
    694             # Create directories that are not part of the archive with
    695             # default permissions.
    696             os.makedirs(upperdirs)
    697 
    698         if tarinfo.islnk() or tarinfo.issym():
    699             self._dbg(1, "%s -> %s" % (tarinfo.name, tarinfo.linkname))
    700         else:
    701             self._dbg(1, tarinfo.name)
    702 
    703         if tarinfo.isreg():
    704             self.makefile(tarinfo, targetpath)
    705         elif tarinfo.isdir():
    706             self.makedir(tarinfo, targetpath)
    707         elif tarinfo.isfifo():
    708             self.makefifo(tarinfo, targetpath)
    709         elif tarinfo.ischr() or tarinfo.isblk():
    710             self.makedev(tarinfo, targetpath)
    711         elif tarinfo.islnk() or tarinfo.issym():
    712             self.makelink(tarinfo, targetpath)
    713         elif tarinfo.type not in SUPPORTED_TYPES:
    714             self.makeunknown(tarinfo, targetpath)
    715         else:
    716             self.makefile(tarinfo, targetpath)
    717 
    718         self.chown(tarinfo, targetpath)
    719         if not tarinfo.issym():
    720             self.chmod(tarinfo, targetpath)
    721             self.utime(tarinfo, targetpath)
    722 
    723     #--------------------------------------------------------------------------
    724     # Below are the different file methods. They are called via
    725     # _extract_member() when extract() is called. They can be replaced in a
    726     # subclass to implement other functionality.
    727 
    728     def makedir(self, tarinfo, targetpath):
    729         """Make a directory called targetpath.
    730         """
    731         try:
    732             # Use a safe mode for the directory, the real mode is set
    733             # later in _extract_member().
    734             os.mkdir(targetpath, 0700)
    735         except EnvironmentError, e:
    736             if e.errno != errno.EEXIST:
    737                 raise
    738 
    739     def makefile(self, tarinfo, targetpath):
    740         """Make a file called targetpath.
    741         """
    742         source = self.extractfile(tarinfo)
    743         try:
    744             with bltn_open(targetpath, "wb") as target:
    745                 copyfileobj(source, target)
    746         finally:
    747             source.close()
    748 
    749     def makeunknown(self, tarinfo, targetpath):
    750         """Make a file from a TarInfo object with an unknown type
    751            at targetpath.
    752         """
    753         self.makefile(tarinfo, targetpath)
    754         self._dbg(1, "tarfile: Unknown file type %r, " \
    755                      "extracted as regular file." % tarinfo.type)
    756 
    757     def makefifo(self, tarinfo, targetpath):
    758         """Make a fifo called targetpath.
    759         """
    760         if hasattr(os, "mkfifo"):
    761             os.mkfifo(targetpath)
    762         else:
    763             raise ExtractError("fifo not supported by system")
    764 
    765     def makedev(self, tarinfo, targetpath):
    766         """Make a character or block device called targetpath.
    767         """
    768         if not hasattr(os, "mknod") or not hasattr(os, "makedev"):
    769             raise ExtractError("special devices not supported by system")
    770 
    771         mode = tarinfo.mode
    772         if tarinfo.isblk():
    773             mode |= stat.S_IFBLK
    774         else:
    775             mode |= stat.S_IFCHR
    776 
    777         os.mknod(targetpath, mode,
    778                  os.makedev(tarinfo.devmajor, tarinfo.devminor))
    779 
    780     def makelink(self, tarinfo, targetpath):
    781         """Make a (symbolic) link called targetpath. If it cannot be created
    782           (platform limitation), we try to make a copy of the referenced file
    783           instead of a link.
    784         """
    785         if hasattr(os, "symlink") and hasattr(os, "link"):
    786             # For systems that support symbolic and hard links.
    787             if tarinfo.issym():
    788                 if os.path.lexists(targetpath):
    789                     os.unlink(targetpath)
    790                 os.symlink(tarinfo.linkname, targetpath)
    791             else:
    792                 # See extract().
    793                 if os.path.exists(tarinfo._link_target):
    794                     if os.path.lexists(targetpath):
    795                         os.unlink(targetpath)
    796                     os.link(tarinfo._link_target, targetpath)
    797                 else:
    798                     self._extract_member(self._find_link_target(tarinfo), targetpath)
    799         else:
    800             try:
    801                 self._extract_member(self._find_link_target(tarinfo), targetpath)
    802             except KeyError:
    803                 raise ExtractError("unable to resolve link inside archive")
    804 
    805     def chown(self, tarinfo, targetpath):
    806         """Set owner of targetpath according to tarinfo.
    807         """
    808         if pwd and hasattr(os, "geteuid") and os.geteuid() == 0:
    809             # We have to be root to do so.
    810             try:
    811                 g = grp.getgrnam(tarinfo.gname)[2]
    812             except KeyError:
    813                 g = tarinfo.gid
    814             try:
    815                 u = pwd.getpwnam(tarinfo.uname)[2]
    816             except KeyError:
    817                 u = tarinfo.uid
    818             try:
    819                 if tarinfo.issym() and hasattr(os, "lchown"):
    820                     os.lchown(targetpath, u, g)
    821                 else:
    822                     if sys.platform != "os2emx":
    823                         os.chown(targetpath, u, g)
    824             except EnvironmentError, e:
    825                 raise ExtractError("could not change owner")
    826 
    827     def chmod(self, tarinfo, targetpath):
    828         """Set file permissions of targetpath according to tarinfo.
    829         """
    830         if hasattr(os, 'chmod'):
    831             try:
    832                 os.chmod(targetpath, tarinfo.mode)
    833             except EnvironmentError, e:
    834                 raise ExtractError("could not change mode")
    835 
    836     def utime(self, tarinfo, targetpath):
    837         """Set modification time of targetpath according to tarinfo.
    838         """
    839         if not hasattr(os, 'utime'):
    840             return
    841         try:
    842             os.utime(targetpath, (tarinfo.mtime, tarinfo.mtime))
    843         except EnvironmentError, e:
    844             raise ExtractError("could not change modification time")
    845 
    846     #--------------------------------------------------------------------------
    847     def next(self):
    848         """Return the next member of the archive as a TarInfo object, when
    849            TarFile is opened for reading. Return None if there is no more
    850            available.
    851         """
    852         self._check("ra")
    853         if self.firstmember is not None:
    854             m = self.firstmember
    855             self.firstmember = None
    856             return m
    857 
    858         # Read the next block.
    859         self.fileobj.seek(self.offset)
    860         tarinfo = None
    861         while True:
    862             try:
    863                 tarinfo = self.tarinfo.fromtarfile(self)
    864             except EOFHeaderError, e:
    865                 if self.ignore_zeros:
    866                     self._dbg(2, "0x%X: %s" % (self.offset, e))
    867                     self.offset += BLOCKSIZE
    868                     continue
    869             except InvalidHeaderError, e:
    870                 if self.ignore_zeros:
    871                     self._dbg(2, "0x%X: %s" % (self.offset, e))
    872                     self.offset += BLOCKSIZE
    873                     continue
    874                 elif self.offset == 0:
    875                     raise ReadError(str(e))
    876             except EmptyHeaderError:
    877                 if self.offset == 0:
    878                     raise ReadError("empty file")
    879             except TruncatedHeaderError, e:
    880                 if self.offset == 0:
    881                     raise ReadError(str(e))
    882             except SubsequentHeaderError, e:
    883                 raise ReadError(str(e))
    884             break
    885 
    886         if tarinfo is not None:
    887             self.members.append(tarinfo)
    888         else:
    889             self._loaded = True
    890 
    891         return tarinfo
    892 
    893     #--------------------------------------------------------------------------
    894     # Little helper methods:
    895 
    896     def _getmember(self, name, tarinfo=None, normalize=False):
    897         """Find an archive member by name from bottom to top.
    898            If tarinfo is given, it is used as the starting point.
    899         """
    900         # Ensure that all members have been loaded.
    901         members = self.getmembers()
    902 
    903         # Limit the member search list up to tarinfo.
    904         if tarinfo is not None:
    905             members = members[:members.index(tarinfo)]
    906 
    907         if normalize:
    908             name = os.path.normpath(name)
    909 
    910         for member in reversed(members):
    911             if normalize:
    912                 member_name = os.path.normpath(member.name)
    913             else:
    914                 member_name = member.name
    915 
    916             if name == member_name:
    917                 return member
    918 
    919     def _load(self):
    920         """Read through the entire archive file and look for readable
    921            members.
    922         """
    923         while True:
    924             tarinfo = self.next()
    925             if tarinfo is None:
    926                 break
    927         self._loaded = True
    928 
    929     def _check(self, mode=None):
    930         """Check if TarFile is still open, and if the operation's mode
    931            corresponds to TarFile's mode.
    932         """
    933         if self.closed:
    934             raise IOError("%s is closed" % self.__class__.__name__)
    935         if mode is not None and self.mode not in mode:
    936             raise IOError("bad operation for mode %r" % self.mode)
    937 
    938     def _find_link_target(self, tarinfo):
    939         """Find the target member of a symlink or hardlink member in the
    940            archive.
    941         """
    942         if tarinfo.issym():
    943             # Always search the entire archive.
    944             linkname = "/".join(filter(None, (os.path.dirname(tarinfo.name), tarinfo.linkname)))
    945             limit = None
    946         else:
    947             # Search the archive before the link, because a hard link is
    948             # just a reference to an already archived file.
    949             linkname = tarinfo.linkname
    950             limit = tarinfo
    951 
    952         member = self._getmember(linkname, tarinfo=limit, normalize=True)
    953         if member is None:
    954             raise KeyError("linkname %r not found" % linkname)
    955         return member
    956 
    957     def __iter__(self):
    958         """Provide an iterator object.
    959         """
    960         if self._loaded:
    961             return iter(self.members)
    962         else:
    963             return TarIter(self)
    964 
    965     def _dbg(self, level, msg):
    966         """Write debugging output to sys.stderr.
    967         """
    968         if level <= self.debug:
    969             print >> sys.stderr, msg
    970 
    971     def __enter__(self):
    972         self._check()
    973         return self
    974 
    975     def __exit__(self, type, value, traceback):
    976         if type is None:
    977             self.close()
    978         else:
    979             # An exception occurred. We must not call close() because
    980             # it would try to write end-of-archive blocks and padding.
    981             if not self._extfileobj:
    982                 self.fileobj.close()
    983             self.closed = True
    984 # class TarFile
    TarFile 源代碼

     

5.6json和pickle模塊

json主要應用於Python中的數據交換,pickle應用於不用的編程語言之間的數據交換。

用於序列化的兩個模塊

  • json,用於字符串 和 python數據類型間進行轉換
  • pickle,用於python特有的類型 和 python的數據類型間進行轉換

Json模塊提供了四個功能:dumps、dump、loads、load

pickle模塊提供了四個功能:dumps、dump、loads、load

下面例子為Python2.7中的例子。

5.7shelve模塊

shelve模塊是一個簡單的k,v將內存數據通過文件持久化的模塊,可以持久化任何pickle可支持的python數據格式。是對pickle的更上一次的封裝。

 1 import shelve
 2  
 3 d = shelve.open('shelve_test') #打開一個文件
 4  
 5 class Test(object):
 6     def __init__(self,n):
 7         self.n = n
 8  
 9  
10 t = Test(123) 
11 t2 = Test(123334)
12  
13 name = ["alex","rain","test"]
14 d["test"] = name #持久化列表
15 d["t1"] = t      #持久化類
16 d["t2"] = t2
17  
18 d.close()

5.8xml模塊

xml是實現不同語言或程序之間進行數據交換的協議,跟json差不多,但json使用起來更簡單,不過,古時候,在json還沒誕生的黑暗年代,大家只能選擇用xml呀,至今很多傳統公司如金融行業的很多系統的接口還主要是xml。

xml的格式如下,就是通過<>節點來區別數據結構的:

 1 <?xml version="1.0"?>
 2 <data>
 3     <country name="Liechtenstein">
 4         <rank updated="yes">2</rank>
 5         <year>2008</year>
 6         <gdppc>141100</gdppc>
 7         <neighbor name="Austria" direction="E"/>
 8         <neighbor name="Switzerland" direction="W"/>
 9     </country>
10     <country name="Singapore">
11         <rank updated="yes">5</rank>
12         <year>2011</year>
13         <gdppc>59900</gdppc>
14         <neighbor name="Malaysia" direction="N"/>
15     </country>
16     <country name="Panama">
17         <rank updated="yes">69</rank>
18         <year>2011</year>
19         <gdppc>13600</gdppc>
20         <neighbor name="Costa Rica" direction="W"/>
21         <neighbor name="Colombia" direction="E"/>
22     </country>
23 </data>
xml模塊

xml協議在各個語言里的都 是支持的,在python中可以用以下模塊操作xml

 1 import xml.etree.ElementTree as ET
 2  
 3 tree = ET.parse("xmltest.xml")
 4 root = tree.getroot()
 5 print(root.tag)
 6  
 7 #遍歷xml文檔
 8 for child in root:
 9     print(child.tag, child.attrib)
10     for i in child:
11         print(i.tag,i.text)
12  
13 #只遍歷year 節點
14 for node in root.iter('year'):
15     print(node.tag,node.text)

修改和刪除xml文檔內容

 1 import xml.etree.ElementTree as ET
 2  
 3 tree = ET.parse("xmltest.xml")
 4 root = tree.getroot()
 5  
 6 #修改
 7 for node in root.iter('year'):
 8     new_year = int(node.text) + 1
 9     node.text = str(new_year)
10     node.set("updated","yes")
11  
12 tree.write("xmltest.xml")
13  
14  
15 #刪除node
16 for country in root.findall('country'):
17    rank = int(country.find('rank').text)
18    if rank > 50:
19      root.remove(country)
20  
21 tree.write('output.xml')

自己 創建Xml

 1 import xml.etree.ElementTree as ET
 2  
 3  
 4 new_xml = ET.Element("namelist")
 5 name = ET.SubElement(new_xml,"name",attrib={"enrolled":"yes"})
 6 age = ET.SubElement(name,"age",attrib={"checked":"no"})
 7 sex = ET.SubElement(name,"sex")
 8 sex.text = '33'
 9 name2 = ET.SubElement(new_xml,"name",attrib={"enrolled":"no"})
10 age = ET.SubElement(name2,"age")
11 age.text = '19'
12  
13 et = ET.ElementTree(new_xml) #生成文檔對象
14 et.write("test.xml", encoding="utf-8",xml_declaration=True)
15  
16 ET.dump(new_xml) #打印生成的格式

5.9PyYAML模塊

pyyaml主要實現配置文檔的書寫。

load一下,編程字典的格式。

Python也可以很容易的處理ymal文檔格式,只不過需要安裝一個模塊,參考文檔:http://pyyaml.org/wiki/PyYAMLDocumentation

5.10ConfigParser模塊

用於生成和修改常見配置文檔,當前模塊的名稱在 python 3.x 版本中變更為 configparser。

一般都是讀配置文件,解析配置文件。修改用的不是很多。

來看一個好多軟件的常見文檔格式如下:

 1 [DEFAULT]
 2 ServerAliveInterval = 45
 3 Compression = yes
 4 CompressionLevel = 9
 5 ForwardX11 = yes
 6  
 7 [bitbucket.org]
 8 User = hg
 9  
10 [topsecret.server.com]
11 Port = 50022
12 ForwardX11 = no

如果想用python生成一個這樣的文檔怎么做呢?

import configparser
 
config = configparser.ConfigParser()
config["DEFAULT"] = {'ServerAliveInterval': '45',
                      'Compression': 'yes',
                     'CompressionLevel': '9'}
 
config['bitbucket.org'] = {}
config['bitbucket.org']['User'] = 'hg'
config['topsecret.server.com'] = {}
topsecret = config['topsecret.server.com']
topsecret['Host Port'] = '50022'     # mutates the parser
topsecret['ForwardX11'] = 'no'  # same here
config['DEFAULT']['ForwardX11'] = 'yes'
with open('example.ini', 'w') as configfile:
   config.write(configfile)

寫完了還可以再讀出來哈。

 1 >>> import configparser
 2 >>> config = configparser.ConfigParser()
 3 >>> config.sections()
 4 []
 5 >>> config.read('example.ini')
 6 ['example.ini']
 7 >>> config.sections()
 8 ['bitbucket.org', 'topsecret.server.com']
 9 >>> 'bitbucket.org' in config
10 True
11 >>> 'bytebong.com' in config
12 False
13 >>> config['bitbucket.org']['User']
14 'hg'
15 >>> config['DEFAULT']['Compression']
16 'yes'
17 >>> topsecret = config['topsecret.server.com']
18 >>> topsecret['ForwardX11']
19 'no'
20 >>> topsecret['Port']
21 '50022'
22 >>> for key in config['bitbucket.org']: print(key)
23 ...
24 user
25 compressionlevel
26 serveraliveinterval
27 compression
28 forwardx11
29 >>> config['bitbucket.org']['ForwardX11']
30 'yes'

configparser增刪改查語法

 1 [section1]
 2 k1 = v1
 3 k2:v2
 4   
 5 [section2]
 6 k1 = v1
 7  
 8 import ConfigParser
 9   
10 config = ConfigParser.ConfigParser()
11 config.read('i.cfg')
12   
13 # ########## 讀 ##########
14 #secs = config.sections()
15 #print secs
16 #options = config.options('group2')
17 #print options
18   
19 #item_list = config.items('group2')
20 #print item_list
21   
22 #val = config.get('group1','key')
23 #val = config.getint('group1','key')
24   
25 # ########## 改寫 ##########
26 #sec = config.remove_section('group1')
27 #config.write(open('i.cfg', "w"))
28   
29 #sec = config.has_section('wupeiqi')
30 #sec = config.add_section('wupeiqi')
31 #config.write(open('i.cfg', "w"))
32   
33   
34 #config.set('group2','k1',11111)
35 #config.write(open('i.cfg', "w"))
36   
37 #config.remove_option('group2','age')
38 #config.write(open('i.cfg', "w"))
View Code

5.11hashlib模塊  

用於加密相關的操作,3.x里代替了md5模塊和sha模塊,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法

MD5不能反解的

import hashlib
 
m = hashlib.md5()
m.update(b"Hello")
m.update(b"It's me")
print(m.digest())
m.update(b"It's been a long time since last time we ...")
 
print(m.digest()) #2進制格式hash
print(len(m.hexdigest())) #16進制格式hash
'''
def digest(self, *args, **kwargs): # real signature unknown
    """ Return the digest value as a string of binary data. """
    pass
 
def hexdigest(self, *args, **kwargs): # real signature unknown
    """ Return the digest value as a string of hexadecimal digits. """
    pass
 
'''
import hashlib
 
# ######## md5 ########
 
hash = hashlib.md5()
hash.update('admin')
print(hash.hexdigest())
 
# ######## sha1 ########
 
hash = hashlib.sha1()
hash.update('admin')
print(hash.hexdigest())
 
# ######## sha256 ########
 
hash = hashlib.sha256()
hash.update('admin')
print(hash.hexdigest())
 
 
# ######## sha384 ########
 
hash = hashlib.sha384()
hash.update('admin')
print(hash.hexdigest())
 
# ######## sha512 ########
 
hash = hashlib.sha512()
hash.update('admin')
print(hash.hexdigest())
View Code

python 還有一個 hmac 模塊,它內部對我們創建 key 和 內容 再進行處理然后再加密

散列消息鑒別碼,簡稱HMAC,是一種基於消息鑒別碼MAC(Message Authentication Code)的鑒別機制。使用HMAC時,消息通訊的雙方,通過驗證消息中加入的鑒別密鑰K來鑒別消息的真偽;

一般用於網絡通信中消息加密,前提是雙方先要約定好key,就像接頭暗號一樣,然后消息發送把用key把消息加密,接收方用key + 消息明文再加密,拿加密后的值 跟 發送者的相對比是否相等,這樣就能驗證消息的真實性,及發送者的合法性了。

 

1 import hmac
2 h = hmac.new(b'天王蓋地虎', b'寶塔鎮河妖')
3 print h.hexdigest()

更多關於md5,sha1,sha256等介紹的文章看這里https://www.tbs-certificates.co.uk/FAQ/en/sha256.html 

5.12Subprocess模塊 

常用subprocess方法示例

#執行命令,返回命令執行狀態 , 0 or 非0
>>> retcode = subprocess.call(["ls", "-l"])

#執行命令,如果命令結果為0,就正常返回,否則拋異常
>>> subprocess.check_call(["ls", "-l"])
0

#接收字符串格式命令,返回元組形式,第1個元素是執行狀態,第2個是命令結果 
>>> subprocess.getstatusoutput('ls /bin/ls')
(0, '/bin/ls')

#接收字符串格式命令,並返回結果
>>> subprocess.getoutput('ls /bin/ls')
'/bin/ls'

#執行命令,並返回結果,注意是返回結果,不是打印,下例結果返回給res
>>> res=subprocess.check_output(['ls','-l'])
>>> res
b'total 0\ndrwxr-xr-x 12 alex staff 408 Nov 2 11:05 OldBoyCRM\n'

#上面那些方法,底層都是封裝的subprocess.Popen
poll()
Check if child process has terminated. Returns returncode

wait()
Wait for child process to terminate. Returns returncode attribute.


terminate() 殺掉所啟動進程
communicate() 等待任務結束

stdin 標准輸入

stdout 標准輸出

stderr 標准錯誤

pid
The process ID of the child process.

#例子
>>> p = subprocess.Popen("df -h|grep disk",stdin=subprocess.PIPE,stdout=subprocess.PIPE,shell=True)
>>> p.stdout.read()
b'/dev/disk1 465Gi 64Gi 400Gi 14% 16901472 104938142 14% /\n'

調用subprocess.run(...)是推薦的常用方法,在大多數情況下能滿足需求,但如果你可能需要進行一些復雜的與系統的交互的話,你還可以用subprocess.Popen(),語法如下:

1 p = subprocess.Popen("find / -size +1000000 -exec ls -shl {} \;",shell=True,stdout=subprocess.PIPE)
2 print(p.stdout.read())

可用參數:

    • args:shell命令,可以是字符串或者序列類型(如:list,元組)
    • bufsize:指定緩沖。0 無緩沖,1 行緩沖,其他 緩沖區大小,負值 系統緩沖
    • stdin, stdout, stderr:分別表示程序的標准輸入、輸出、錯誤句柄
    • preexec_fn:只在Unix平台下有效,用於指定一個可執行對象(callable object),它將在子進程運行之前被調用
    • close_sfs:在windows平台下,如果close_fds被設置為True,則新創建的子進程將不會繼承父進程的輸入、輸出、錯誤管道。
      所以不能將close_fds設置為True同時重定向子進程的標准輸入、輸出與錯誤(stdin, stdout, stderr)。
    • shell:同上
    • cwd:用於設置子進程的當前目錄
    • env:用於指定子進程的環境變量。如果env = None,子進程的環境變量將從父進程中繼承。
    • universal_newlines:不同系統的換行符不同,True -> 同意使用 \n
    • startupinfo與createionflags只在windows下有效
      將被傳遞給底層的CreateProcess()函數,用於設置子進程的一些屬性,如:主窗口的外觀,進程的優先級等等

終端輸入的命令分為兩種:

  • 輸入即可得到輸出,如:ifconfig
  • 輸入進行某環境,依賴再輸入,如:python

需要交互的命令示例

import subprocess
 
obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
obj.stdin.write('print 1 \n ')
obj.stdin.write('print 2 \n ')
obj.stdin.write('print 3 \n ')
obj.stdin.write('print 4 \n ')
 
out_error_list = obj.communicate(timeout=10)
print out_error_list

subprocess實現sudo 自動輸入密碼

 1 import subprocess
 2  
 3 def mypass():
 4     mypass = '123' #or get the password from anywhere
 5     return mypass
 6  
 7 echo = subprocess.Popen(['echo',mypass()],
 8                         stdout=subprocess.PIPE,
 9                         )
10  
11 sudo = subprocess.Popen(['sudo','-S','iptables','-L'],
12                         stdin=echo.stdout,
13                         stdout=subprocess.PIPE,
14                         )
15  
16 end_of_pipe = sudo.stdout
17  
18 print "Password ok \n Iptables Chains %s" % end_of_pipe.read()

5.13logging模塊

很多程序都有記錄日志的需求,並且日志中包含的信息即有正常的程序訪問日志,還可能有錯誤、警告等信息輸出,python的logging模塊提供了標准的日志接口,你可以通過它存儲各種格式的日志,logging的日志可以分為 debug()info()warning()error() and critical() 5個級別,下面我們看一下怎么用。

1 import logging
2  
3 logging.warning("user [alex] attempted wrong password more than 3 times")
4 logging.critical("server is down")
5  
6 #輸出
7 WARNING:root:user [alex] attempted wrong password more than 3 times
8 CRITICAL:root:server is down

如果想把日志寫到文件里,也很簡單

1 import logging
2  
3 logging.basicConfig(filename='example.log',level=logging.INFO)
4 logging.debug('This message should go to the log file')
5 logging.info('So should this')
6 logging.warning('And this, too')

其中下面這句中的level=loggin.INFO意思是,把日志紀錄級別設置為INFO,也就是說,只有比日志是INFO或比INFO級別更高的日志才會被紀錄到文件里,在這個例子, 第一條日志是不會被紀錄的,如果希望紀錄debug的日志,那把日志級別改成DEBUG就行了。

logging.basicConfig(filename='example.log',level=logging.INFO)

感覺上面的日志格式忘記加上時間啦,日志不知道時間怎么行呢,下面就來加上!

1 import logging
2 logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
3 logging.warning('is when this event was logged.')
4  
5 #輸出
6 12/12/2010 11:46:36 AM is when this event was logged.

如果想同時把log打印在屏幕和文件日志里,就需要了解一點復雜的知識 了


Python 使用logging模塊記錄日志涉及四個主要類,使用官方文檔中的概括最為合適:

logger提供了應用程序可以直接使用的接口;

handler將(logger創建的)日志記錄發送到合適的目的輸出;

filter提供了細度設備來決定輸出哪條日志記錄;

formatter決定日志記錄的最終輸出格式。

 

日志的格式

%(name)s

Logger的名字

%(levelno)s

數字形式的日志級別

%(levelname)s

文本形式的日志級別

%(pathname)s

調用日志輸出函數的模塊的完整路徑名,可能沒有

%(filename)s

調用日志輸出函數的模塊的文件名

%(module)s

調用日志輸出函數的模塊名

%(funcName)s

調用日志輸出函數的函數名

%(lineno)d

調用日志輸出函數的語句所在的代碼行

%(created)f

當前時間,用UNIX標准的表示時間的浮 點數表示

%(relativeCreated)d

輸出日志信息時的,自Logger創建以 來的毫秒數

%(asctime)s

字符串形式的當前時間。默認格式是 “2003-07-08 16:49:45,896”。逗號后面的是毫秒

%(thread)d

線程ID。可能沒有

%(threadName)s

線程名。可能沒有

%(process)d

進程ID。可能沒有

%(message)s

用戶輸出的消息

logger
每個程序在輸出信息之前都要獲得一個Logger。Logger通常對應了程序的模塊名,比如聊天工具的圖形界面模塊可以這樣獲得它的Logger:
LOG=logging.getLogger(”chat.gui”)
而核心模塊可以這樣:
LOG=logging.getLogger(”chat.kernel”)

Logger.setLevel(lel):指定最低的日志級別,低於lel的級別將被忽略。debug是最低的內置級別,critical為最高
Logger.addFilter(filt)、Logger.removeFilter(filt):添加或刪除指定的filter
Logger.addHandler(hdlr)、Logger.removeHandler(hdlr):增加或刪除指定的handler
Logger.debug()、Logger.info()、Logger.warning()、Logger.error()、Logger.critical():可以設置的日志級別

 

handler

handler對象負責發送相關的信息到指定目的地。Python的日志系統有多種Handler可以使用。有些Handler可以把信息輸出到控制台,有些Logger可以把信息輸出到文件,還有些 Handler可以把信息發送到網絡上。如果覺得不夠用,還可以編寫自己的Handler。可以通過addHandler()方法添加多個多handler
Handler.setLevel(lel):指定被處理的信息級別,低於lel級別的信息將被忽略
Handler.setFormatter():給這個handler選擇一個格式
Handler.addFilter(filt)、Handler.removeFilter(filt):新增或刪除一個filter對象


每個Logger可以附加多個Handler。接下來我們就來介紹一些常用的Handler:
1) logging.StreamHandler
使用這個Handler可以向類似與sys.stdout或者sys.stderr的任何文件對象(file object)輸出信息。它的構造函數是:
StreamHandler([strm])
其中strm參數是一個文件對象。默認是sys.stderr


2) logging.FileHandler
和StreamHandler類似,用於向一個文件輸出日志信息。不過FileHandler會幫你打開這個文件。它的構造函數是:
FileHandler(filename[,mode])
filename是文件名,必須指定一個文件名。
mode是文件的打開方式。參見Python內置函數open()的用法。默認是’a',即添加到文件末尾。

3) logging.handlers.RotatingFileHandler
這個Handler類似於上面的FileHandler,但是它可以管理文件大小。當文件達到一定大小之后,它會自動將當前日志文件改名,然后創建 一個新的同名日志文件繼續輸出。比如日志文件是chat.log。當chat.log達到指定的大小之后,RotatingFileHandler自動把 文件改名為chat.log.1。不過,如果chat.log.1已經存在,會先把chat.log.1重命名為chat.log.2。。。最后重新創建 chat.log,繼續輸出日志信息。它的構造函數是:
RotatingFileHandler( filename[, mode[, maxBytes[, backupCount]]])
其中filename和mode兩個參數和FileHandler一樣。
maxBytes用於指定日志文件的最大文件大小。如果maxBytes為0,意味着日志文件可以無限大,這時上面描述的重命名過程就不會發生。
backupCount用於指定保留的備份文件的個數。比如,如果指定為2,當上面描述的重命名過程發生時,原有的chat.log.2並不會被更名,而是被刪除。


4) logging.handlers.TimedRotatingFileHandler
這個Handler和RotatingFileHandler類似,不過,它沒有通過判斷文件大小來決定何時重新創建日志文件,而是間隔一定時間就 自動創建新的日志文件。重命名的過程與RotatingFileHandler類似,不過新的文件不是附加數字,而是當前時間。它的構造函數是:
TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]])
其中filename參數和backupCount參數和RotatingFileHandler具有相同的意義。
interval是時間間隔。
when參數是一個字符串。表示時間間隔的單位,不區分大小寫。它有以下取值:
S 秒
M 分
H 小時
D 天
W 每星期(interval==0時代表星期一)
midnight 每天凌晨

 1 import logging
 2  
 3 #create logger
 4 logger = logging.getLogger('TEST-LOG')
 5 logger.setLevel(logging.DEBUG)
 6  
 7  
 8 # create console handler and set level to debug
 9 ch = logging.StreamHandler()
10 ch.setLevel(logging.DEBUG)
11  
12 # create file handler and set level to warning
13 fh = logging.FileHandler("access.log")
14 fh.setLevel(logging.WARNING)
15 # create formatter
16 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
17  
18 # add formatter to ch and fh
19 ch.setFormatter(formatter)
20 fh.setFormatter(formatter)
21  
22 # add ch and fh to logger
23 logger.addHandler(ch)
24 logger.addHandler(fh)
25  
26 # 'application' code
27 logger.debug('debug message')
28 logger.info('info message')
29 logger.warn('warn message')
30 logger.error('error message')
31 logger.critical('critical message')
View Code

文件自動截斷例子

 1 import logging
 2 
 3 from logging import handlers
 4 
 5 logger = logging.getLogger(__name__)
 6 
 7 log_file = "timelog.log"
 8 #fh = handlers.RotatingFileHandler(filename=log_file,maxBytes=10,backupCount=3)
 9 fh = handlers.TimedRotatingFileHandler(filename=log_file,when="S",interval=5,backupCount=3)
10 
11 
12 formatter = logging.Formatter('%(asctime)s %(module)s:%(lineno)d %(message)s')
13 
14 fh.setFormatter(formatter)
15 
16 logger.addHandler(fh)
17 
18 
19 logger.warning("test1")
20 logger.warning("test12")
21 logger.warning("test13")
22 logger.warning("test14")
View Code

5.14 RE模塊

常用正則表達式符號

 1 '.'     默認匹配除\n之外的任意一個字符,若指定flag DOTALL,則匹配任意字符,包括換行
 2 '^'     匹配字符開頭,若指定flags MULTILINE,這種也可以匹配上(r"^a","\nabc\neee",flags=re.MULTILINE)
 3 '$'     匹配字符結尾,或e.search("foo$","bfoo\nsdfsf",flags=re.MULTILINE).group()也可以
 4 '*'     匹配*號前的字符0次或多次,re.findall("ab*","cabb3abcbbac")  結果為['abb', 'ab', 'a']
 5 '+'     匹配前一個字符1次或多次,re.findall("ab+","ab+cd+abb+bba") 結果['ab', 'abb']
 6 '?'     匹配前一個字符1次或0次
 7 '{m}'   匹配前一個字符m次
 8 '{n,m}' 匹配前一個字符n到m次,re.findall("ab{1,3}","abb abc abbcbbb") 結果'abb', 'ab', 'abb']
 9 '|'     匹配|左或|右的字符,re.search("abc|ABC","ABCBabcCD").group() 結果'ABC'
10 '(...)' 分組匹配,re.search("(abc){2}a(123|456)c", "abcabca456c").group() 結果 abcabca456c
11  
12  
13 '\A'    只從字符開頭匹配,re.search("\Aabc","alexabc") 是匹配不到的
14 '\Z'    匹配字符結尾,同$
15 '\d'    匹配數字0-9
16 '\D'    匹配非數字
17 '\w'    匹配[A-Za-z0-9]
18 '\W'    匹配非[A-Za-z0-9]
19 's'     匹配空白字符、\t、\n、\r , re.search("\s+","ab\tc1\n3").group() 結果 '\t'
20  
21 '(?P<name>...)' 分組匹配 re.search("(?P<province>[0-9]{4})(?P<city>[0-9]{2})(?P<birthday>[0-9]{4})","371481199306143242").groupdict("city") 結果{'province': '3714', 'city': '81', 'birthday': '1993'}

最常用的匹配語法

1 re.match 從頭開始匹配
2 re.search 匹配包含
3 re.findall 把所有匹配到的字符放到以列表中的元素返回
4 re.split 以匹配到的字符當做列表分隔符
5 re.sub      匹配字符並替換

反斜杠的困擾

轉義\ 和分組(2)
與大多數編程語言相同,正則表達式里使用"\"作為轉義字符,這就可能造成反斜杠困擾。假如你需要匹配文本中的字符"\",那么使用編程語言表示的正則表達式里將需要4個反斜杠"\\\\":前兩個和后兩個分別用於在編程語言里轉義成反斜杠,轉換成兩個反斜杠后再在正則表達式里轉義成一個反斜杠。Python里的原生字符串很好地解決了這個問題,這個例子中的正則表達式可以使用r"\\"表示。同樣,匹配一個數字的"\\d"可以寫成r"\d"。有了原生字符串,你再也不用擔心是不是漏寫了反斜杠,寫出來的表達式也更直觀。

 

僅需輕輕知道的幾個匹配模式

1 re.I(re.IGNORECASE): 忽略大小寫(括號內是完整寫法,下同)
2 M(MULTILINE): 多行模式,改變'^''$'的行為
3 S(DOTALL): 點任意匹配模式,改變'.'的行為

 六、作業

6.1  開發一個簡單的python計算器

  1. 實現加減乘除及拓號優先級解析
  2. 用戶輸入 1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )等類似公式后,必須自己解析里面的(),+,-,*,/符號和公式(不能調用eval等類似功能偷懶實現),運算后得出結果,結果必須與真實的計算器所得出的結果一致

 6.2模塊化練習:

#實現效果:隨機生成一個6位驗證碼(包含數字和字母)
1 import random
2 def make_code(n):
3     res=''
4     for i in range(n):
5         s1=chr(random.randint(65,90))
6         s2=str(random.randint(0,9))
7         res+=random.choice([s1,s2])
8     return res
9 print(make_code(6))

6.3 模擬一個搶紅包的例子, 實現效果: 10塊錢,隨機分層5份,要求精確到分。

 1 num=5
 2 total=1000#先擴大100倍,在縮小100倍 剩余的錢
 3 list_1=[]
 4 for i in range(4):
 5     a=random.randint(1,total-num+1)
 6     list_1.append(a/100)
 7     total=total-a
 8     num=num-1
 9 list_1.append(total/100)
10 print(list_1)
參考答案

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM