鍍金池/ 教程/ Python/ 文件與目錄
附錄
進(jìn)程通信
操作系統(tǒng)
迭代器
模塊
描述符
裝飾器
第三部分 擴展庫
內(nèi)置類型
數(shù)據(jù)存儲
數(shù)據(jù)類型
基本環(huán)境
文件與目錄
異常
程序框架
數(shù)學(xué)運算
函數(shù)
元類
字符串
表達(dá)式

文件與目錄

file

通常建議用內(nèi)置函數(shù) open() 打開文件,file 用于類型判斷。

>>> with open("test.txt", "w") as f:
...     print isinstance(f, file)   // 類型判斷
...     f.writelines(map(str, range(10)))

True

File Object 實現(xiàn)了上下文協(xié)議,可確保文件被及時關(guān)閉。實際上,文件對象被回收時總是會調(diào)用 close 方法,所以可以寫下面這樣的代碼。

>>> open("test.txt", "r").read()
'0123456789'

如果要把數(shù)據(jù)寫到磁盤上,除調(diào)用 flush() 外,還得用 sync(),以確保數(shù)據(jù)從系統(tǒng)緩沖區(qū)同步到磁盤。close() 總是會調(diào)用這兩個方法。

打開模式:

  • r: 只讀。
  • w: 只寫。已存在文件將被清除 (truncate)。
  • a: 添加??偸翘砑拥轿募膊俊?/li>
  • b: 二進(jìn)制模式。
  • r+: 更新文件,可讀寫,不會截短文件。
  • w+: 更新文件,可讀寫,清除原有內(nèi)容。
  • a+: 更新文件,可讀寫,總是在尾部添加。

文件對象還實現(xiàn)了迭代器協(xié)議,可直接循環(huán)獲取其內(nèi)容。

>>> with open("main.py", "r") as f:
...     for line in f: print line
...

讀方法總能判斷不同平臺的換行標(biāo)記,但寫方法不會添加任何換行字符,包括 writelines。

>>> with open("test.txt", "w") as f:
...     f.write("a")
...     f.writelines("bc")

>>> cat test.txt
abc

如必須按不同平臺寫入換行標(biāo)記,可使用 os.linesep。

>>> os.linesep
'\n'

字符串本身就是序列類型,可以直接用 writelines(str)。readline() 會返回包括換行符在內(nèi)的整個行數(shù)據(jù)。通常建議用迭代器或 xreadlines() 代替 readlines(),后者默認(rèn)一次性讀取整個文件。

binary

用 struct 將其他類型構(gòu)建成二進(jìn)制字節(jié)數(shù)組,然后寫入文件即可。

>>> import struct

>>> data = struct.pack("2i2s", 0x1234, 0xFF56, "ab")
>>> open("test.dat", "w").write(data)

>>> !xxd -g 1 test.dat
0000000: 34 12 00 00 56 ff 00 00 61 62 4...V...ab

>>> struct.unpack("2i2s", open("test.dat").read())
(4660, 65366, 'ab')

>>> with open("test.dat") as f:   // 結(jié)構(gòu)化讀取
...     def xread(fmt):
...         n = struct.calcsize(fmt)  // 計算長度
...         s = f.read(n)
...         return struct.unpack(fmt, s)
...     print xread("i")
...     print xread("i")
...     print xread("2s")

(4660,)
(65366,)
('ab',)

對于相同類型的數(shù)據(jù),可考慮用 array,以獲得更好的性能。

>>> import array

>>> datas = array.array("i")
>>> datas.append(0x1234)
>>> datas.append(0xFF56)
>>> datas.tofile(open("test.dat", "w"))

>>> !xxd -g 1 test.dat
0000000: 34 12 00 00 56 ff 00 00 4...V...

>>> d2 = array.array("i")
>>> d2.fromfile(open("test.dat"), 2)
>>> d2
array('i', [4660, 65366])

類似的還有 bytearray,可作 Buffer 用,詳情參見 struct 章節(jié)。

encoding

標(biāo)準(zhǔn)庫 codecs 提供了一個包裝版的 open(),可自動完成編碼轉(zhuǎn)換工作。

>>> import sys
>>> reload(sys)
>>> sys.setdefaultencoding("utf-8")

>>> with codecs.open("test.txt", "w", "gbk") as f:
...     f.write("中國")

>>> !xxd -g 1 test.txt
0000000: d6 d0 b9 fa ....

>>> "中國".encode("gbk")
'\xd6\xd0\xb9\xfa'

>>> s = codecs.open("test.txt", encoding = "gbk").read()
>>> s
u'\u4e2d\u56fd'
>>> print s
中國

descriptor

除使用文件對象外,某些時候還可能需要直接操控文件描述符。

>>> import os

>>> fd = os.open("test.txt", os.O_CREAT | os.O_RDWR, 0644) // 注意是八進(jìn)制。

>>> ls -l test.txt
-rw-r--r-- 1 yuhen staff 6 3 25 10:45 test.txt

>>> os.write(fd, "abc")
3

>>> f = os.fdopen(fd, "r+")  // 通過描述符創(chuàng)建文件對象。
>>> f.seek(0, os.SEEK_SET)  // 注意調(diào)整位置。
>>> f.read()
'abc'
>>> f.write("123")
>>> f.flush()    // os 庫提供的函數(shù)是系統(tǒng)調(diào)用,因此需要把數(shù)據(jù)從用戶緩存
      // 刷新到系統(tǒng)緩存。

>>> os.lseek(fd, 0, os.SEEK_SET)
0

>>> os.read(fd, 100)
'abc123'

>>> os.close(fd)    // 通常建議用和打開對應(yīng)的方式關(guān)閉。

文件對象 fileno() 方法返回其對應(yīng)的文件描述符。

tempfile

Python 對臨時文件的支持算是我所見過語言中最豐富的。通常建議使用 NamedTemporaryFile,其他可以忽略。

  • TemporaryFile: 創(chuàng)建臨時文件對象,關(guān)閉時自動刪除。
  • NamedTemporaryFile: 創(chuàng)建臨時文件對象,可獲取文件名,參數(shù)決定是否自動刪除。
  • SpooledTemporaryFile: 和 TemporaryFile 類似,只有在數(shù)據(jù)超過閾值時,才寫入硬盤。
>>> import tempfile, os.path

>>> tmp = tempfile.NamedTemporaryFile()
>>> tmp.name
'/var/folders/r2/4vkjhz6s6lz02hk6nh2qb99c0000gn/T/tmpYYB6p3'

>>> os.path.exists(tmp.name)
True

>>> tmp.close()
>>> os.path.exists(tmp.name)
False

默認(rèn)使用系統(tǒng)臨時目錄和前綴,當(dāng)然也可以指定不同的配置。

>>> with tempfile.NamedTemporaryFile(prefix = "xxx_", suffix = ".tmp", dir = ".") as f:
...     print f.name
...
/Users/yuhen/test/xxx_SL3apY.tmp

與臨時文件有關(guān)的函數(shù)還有:

  • tempfile.gettempdir: 返回系統(tǒng)臨時文件存放路徑。
  • tempfile.gettempprefix: 返回默認(rèn)的臨時文件名前綴。
  • tempfile.mkdtemp: 創(chuàng)建臨時目錄。
  • tempfile.mkstemp: 創(chuàng)建臨時文件,返回描述符和文件名,需手工刪除。
  • os.tempnam: 僅返回有效的臨時文件名,并不創(chuàng)建文件。
  • os.tmpfile(): 創(chuàng)建臨時文件對象,關(guān)閉后自動刪除。
>>> tempfile.gettempdir()
'/var/folders/r2/4vkjhz6s6lz02hk6nh2qb99c0000gn/T'

>>> tempfile.gettempprefix()
'tmp'
>>> d = tempfile.mkdtemp(); d
'/var/folders/r2/4vkjhz6s6lz02hk6nh2qb99c0000gn/T/tmpE_bRWd'

>>> os.path.exists(d)
True

>>> os.removedirs(d)
>>> fd, name = tempfile.mkstemp()

>>> os.write(fd, "123\n")
4

>>> os.close(fd)

>>> os.path.exists(name)
True

>>> os.remove(name)

os.path

常用函數(shù)列表:

http://wiki.jikexueyuan.com/project/the-python-study-notes-second-edition/images/5.png" alt="" />

http://wiki.jikexueyuan.com/project/the-python-study-notes-second-edition/images/6.png" alt="" />

拼接的目錄看上亂糟糟讓人煩心。

>>> os.path.normpath("./../a/b/../c")
'../a/c'

展開用戶根路徑,或者包含系統(tǒng)環(huán)境變量的路徑。

>>> os.path.expanduser("~/.vimrc")
'/Users/yuhen/.vimrc'

>>> os.path.expandvars("$HOME/.vimrc")
'/Users/yuhen/.vimrc'

除非只要擴展名,否則還是先用 basename 將路徑去掉。

>>> os.path.splitext(os.path.basename("/usr/local/lib/libevent.a"))
('libevent', '.a')

os

常用函數(shù)列表:

http://wiki.jikexueyuan.com/project/the-python-study-notes-second-edition/images/7.png" alt="" />

http://wiki.jikexueyuan.com/project/the-python-study-notes-second-edition/images/8.png" alt="" />

迭代 walk,返回 "(路徑,子目錄列表,文件列表)",可配合 fnmatch 做通配符過濾。

>>> for path, dirs, files in os.walk("."):
...     for f in files:
...         if fnmatch.fnmatch(f, "*.py"):
...             print os.path.join(path, f)

./main.py
./bak/amqplib_test.py
./bak/eventlet_test.py
./bak/extract_text.py
./bak/fabric_test.py

如果僅操作當(dāng)前目錄,可以用 glob 代替 listdir,前者支持通配符。

>>> glob.glob("./bak/[rs]*.py")    # 迭代器版本: iglob
['./bak/redis_test.py', './bak/socket_test.py']

如目錄中還有文件存在,removedirs 會拋出異常。建議用 shutil.rmtree() 代替,注意參數(shù)區(qū)別。

>>> os.makedirs("./a/b/c")
>>> open("./a/b/c/test.txt", "w").write("abc")

>>> os.removedirs("./a/b/c")
OSError: [Errno 66] Directory not empty: './a/b/c'

>>> import shutil
>>> shutil.rmtree("./a")

某些時候,需要先測試文件是否擁有某些權(quán)限。

>>> os.access("a.txt", os.W_OK)
True

都是哪些人需要修改文件時間?

>>> stat -x a.txt
    File: "a.txt"
    Size: 0 FileType: Regular File
    Mode: (0644/-rw-r--r--) Uid: ( 501/ yuhen) Gid: ( 20/ staff)
Device: 1,2 Inode: 5111644 Links: 1
Access: Mon Mar 25 17:43:01 2013
Modify: Mon Mar 25 17:43:01 2013
Change: Mon Mar 25 17:43:01 2013
>>> atime = time.mktime(datetime.datetime(2010, 10, 1).utctimetuple())
>>> mtime = time.mktime(datetime.datetime(2010, 11, 2).utctimetuple())
>>> os.utime("a.txt", (atime, mtime))

>>> os.stat("a.txt").st_atime == atime
True

獲取文件權(quán)限信息時,別忘了轉(zhuǎn)換成八進(jìn)制。

>>> oct(os.stat("a.txt").st_mode)
'0100644'

shutil

常用函數(shù)列表:

http://wiki.jikexueyuan.com/project/the-python-study-notes-second-edition/images/9.png" alt="" />

copytree 可以指定多個忽略通配符,且必須確保目標(biāo)路徑不存在。

>>> shutil.copytree("./bak", "./b/bak", ignore = shutil.ignore_patterns("*.pyc",
"*.bak"))
下一篇:表達(dá)式