用 Python 的輸入輸出功能讀取和寫入數據

讀取、寫入和 Python

在 “探索 Python” 系列以前的文章中,學習了基本的 Python 數據類型和一些容器數據類型,例如 tuplestring 和 list。其他文章討論了 Python 語言的條件和循環特性,以及它們如何與容器數據類型進行協作來簡化編程任務。編寫程序的最後一個基本步驟就是從文件讀取數據和把數據寫入文件。閱讀完這篇文章之後,可以在自己的 to-do 列表中加上檢驗這個技能學習效果的任務。

簡單輸出

貫穿整個系列,一直用 print 語句寫入(輸出)數據,它默認把表達式作爲 string 寫到屏幕上(或控制檯窗口上)。清單 1 演示了這一點。清單 1 重複了第一個 Python 程序 “Hello, World!”,但是做了一些小的調整。

清單 1. 簡單輸出
1
2
3
4
5
6
7
8
9
10
>>> print "Hello World!"
Hello World!
>>> print "The total value is = $", 40.0*45.50
The total value is = $ 1820.0
>>> print "The total value = $%6.2f" % (40.0*45.50)
The total value = $1820.00
>>> myfile = file("testit.txt", 'w')
>>> print >> myfile, "Hello World!"
>>> print >> myfile, "The total value = $%6.2f" % (40.0*45.50)
>>> myfile.close()

正如這個示例演示的,用 print 語句寫入數據很容易。首先,示例輸出一個簡單的 string。然後創建並輸出複合的 string,這個字符串是用 string 格式化技術創建的。

但是,在這之後,事情發生了變化,與代碼以前的版本不同。接下來的一行創建 file 對象,傳遞進名稱 "testit.txt" 和 'w'字符(寫入文件)。然後使用修改過的 print 語句 —— 兩個大於號後邊跟着容納 file 對象的變量 —— 寫入相同的 string。但是這一次,數據不是在屏幕上顯示。很自然的問題是:數據去哪兒了?而且,這個 file 對象是什麼?

第一個問題很容易回答。請查找 testit.txt 文件,並像下面那樣顯示它的內容。

1
2
3
% more testit.txt
Hello World!
The total value = $1820.00

可以看到,數據被準確地寫入文件,就像以前寫到屏幕上一樣。

現在,請注意清單 1 中的最後一行,它調用 file 對象的 close 方法。在 Python 程序中這很重要,因爲在默認情況下,文件輸入和輸出是緩衝的;在調用 print 語句時,數據實際未被寫入;相反,數據是成批寫入的。讓 Python 把數據寫入文件的最簡單方式就是顯式地調用 close 方法。

文件對象

file 是與計算機上的文件進行交互的基本機制。可以用 file 對象讀取數據、寫入數據或把數據添加到文件,以及處理二進制或文本數據。

學習 file 對象的最簡單方法就是閱讀幫助,如清單 2 所示。

清單 2. 得到 file 對象的幫助
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
>>> help(file)
Help on class file in module __builtin__:
class file(object)
 |  file(name[, mode[, buffering]]) -> file object
 
 |  Open a file.  The mode can be 'r', 'w' or 'a' for reading (default),
 |  writing or appending.  The file will be created if it doesn't exist
 |  when opened for writing or appending; it will be truncated when
 |  opened for writing.  Add a 'b' to the mode for binary files.
 |  Add a '+' to the mode to allow simultaneous reading and writing.
 |  If the buffering argument is given, 0 means unbuffered, 1 means line
 |  buffered, and larger numbers specify the buffer size.
 |  Add a 'U' to mode to open the file for input with universal newline
 |  support.  Any line ending in the input file will be seen as a '\n'
 |  in Python.  Also, a file so opened gains the attribute 'newlines';
 |  the value for this attribute is one of None (no newline read yet),
 |  '\r', '\n', '\r\n' or a tuple containing all the newline types seen.
 
 |  'U' cannot be combined with 'w' or '+' mode.
 
 |  Note:  open() is an alias for file().
 
 |  Methods defined here:
...

正如幫助工具指出的,使用 file 對象很簡單。用 file 構造函數或 open 方法創建 file 對象,open 是 file 構造函數的別名。第二個參數是可選的,它指定文件的使用方式:

  • 'r' (默認值)表示從文件讀取數據。
  • 'w' 表示要向文件寫入數據,並截斷以前的內容。
  • 'a' 表示要向文件寫入數據,但是添加到當前內容尾部。
  • 'r+' 表示對文件進行讀寫操作(刪除以前的所有數據)。
  • 'r+a' 表示對文件進行讀寫操作(添加到當前內容尾部)。
  • 'b' 表示要讀寫二進制數據。

這篇文章的第一個代碼清單向文件寫入數據。現在,清單 3 顯示如何把這個數據讀入 Python 程序,並解析文件的內容。

清單 3. 從文件讀取數據
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>>> myfile = open("testit.txt")
>>> myfile.read()
'Hello World!\nThe total value = $1820.00\n'
>>> str = myfile.read()
>>> print str
>>> myfile.seek(0)
>>> str = myfile.read()
>>> print str
Hello World!
The total value = $1820.00
>>> str.split()
['Hello', 'World!', 'The', 'total', 'value', '=', '$1820.00']
>>> str.split('\n')
['Hello World!', 'The total value = $1820.00', '']
>>> for line in str.split('\n'):
...     print line
...
Hello World!
The total value = $1820.00
>>> myfile.close()

要讀取數據,首先要創建合適的 file 對象 —— 在這個示例中,文件對象打開 testit.txt 文件,並用 read 方法讀取內容。這個方法把整個文件讀入一個 string,然後在程序中把這個字符串輸出到控制檯。在對 read 方法的第二個調用中,試圖把值分配給 str 變量,結果返回一個空的 string。這是因爲第一個 read 操作讀入了整個文件。當試圖再次讀取內容時,已經到了文件末尾,所以什麼也讀不到。

這個問題的解決方案也很簡單:讓 file 對象返回文件的開頭。回到開頭要通過 seek 方法進行,它接受一個參數,表示要從文件中的什麼位置開始讀取或寫入(例如,0 代表文件開頭)。seek 方法支持更復雜的操作,但是可能會有危險。對於目前來說,我們還堅持採用簡單方式。

現在回到了文件的開始之處,可以把文件內容讀入 string 變量並對 string 做適當地解析。請注意,在文件中,行之間用新行(或行結束)字符區分。如果試着在 string 上調用 split 方法,它會在空白字符(例如空格)處進行拆分。爲了讓方法根據新行字符拆分各行,必須顯式地指定新行字符。然後可以拆分 string 並在 for 循環中對文件的行進行迭代。

看起來僅僅從文件中讀取和處理一行內容都有許多工作要做。Python 要讓簡單的事情變容易,所以您可能想知道這個任務有沒有快捷方式可用。如清單 4 所示,答案是 yes。

清單 4. 讀取和解析行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> myfile = open("testit.txt")
>>> for line in myfile.readlines():
...     print line
...
Hello World!
The total value = $1820.00
>>> myfile.close()
>>> for line in open("testit.txt").readlines():
...     print line
...
Hello World!
The total value = $1820.00
>>> for line in open("testit.txt"):
...     print line
...
Hello World!
The total value = $1820.00

清單 4 演示了讀取和解析文本文件行的三種技術。首先,打開文件並把它分配給變量。然後調用 readlines 方法,把整個文件讀入內存並把內容拆分成 string 列表。for 循環在 string 列表上進行迭代,一次輸出一行。

第二個 for 循環通過使用 file 對象的隱式變量(也就是說,變量不是顯式創建的),對這個過程稍做了點兒簡化。打開文件和讀取文件內容一次完成,生成的效果與第一個顯式示例相同。最後一個示例進一步做了簡化,並演示了直接在 file 對象上進行迭代的能力(請注意,這是 Python 的一個新特性,所以在您的計算機上可能無法工作)。在這個示例中,創建隱式 file對象,然後 Python 做餘下的工作,允許對文件中的全部行進行迭代。

但是,有些時候,在從文件讀取數據時,可能想要更好的控制級別。在這種情況下,應當使用 readline 方法,如清單 5 所示。

清單 5. 讀取數據
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
>>> myfile = open("testit.txt")
>>> myfile.readline()
'Hello World!\n'
>>> myfile.readline()
'The total value = $1820.00\n'
>>> myfile.readline()
''
>>> myfile.seek(0)
>>> myfile.readline()
'Hello World!\n'
>>> myfile.tell()
13L
>>> myfile.readline()
'The total value = $1820.00\n'
>>> myfile.tell()
40L
>>> myfile.readline()
''
>>> myfile.tell()
40L
>>> myfile.seek(0)
>>> myfile.read(17)
'Hello World!\nThe '
>>> myfile.seek(0)
>>> myfile.readlines(23)
['Hello World!\n', 'The total value = $1820.00\n']
>>> myfile.close()

這個示例演示瞭如何在文件中移動,一次讀取一行,或者顯式地用 seek 方法移動文件位置指示器。首先,用 readline 方法在文件行中移動。當到達文件末尾時,readline 方法返回一個空的 string。在過了文件末尾之後,如果還用這種方式繼續讀取,並不會造成錯誤,只會返回空的 string

然後返回文件開始的地方,並讀取另一行。 tell 方法顯示出在文件中的當前位置(應當在第一行文本之後) —— 在這個示例中,在第 13 個字符位置。通過使用這個知識,可以向 read 或readline 方法傳遞一個參數,控制讀取的字符數。對於 read方法,這個參數(在這個示例中是 17)是要從文件中讀取的字符數。但是 readline 方法在讀入指定數量的字符後,還會繼續讀取,直到行尾。在這個示例中,它讀取第一行和第二行文本。

寫入數據

迄今爲止的示例都側重於讀取數據,而不是寫入數據。但是如清單 6 所示,一旦瞭解了使用 file 對象的基礎知識,寫入也很容易。

清單 6. 寫入數據
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
>>> mydata = ['Hello World!', 'The total value = $1820.00']
>>> myfile = open('testit.txt', 'w')
>>> for line in mydata:
...     myfile.write(line + '\n')
...
>>> myfile.close()
>>> myfile = open("testit.txt")
>>> myfile.read()
'Hello World!\nThe total value = $1820.00\n'
>>> myfile.close()
>>> myfile = open("testit.txt", "r+")
>>> for line in mydata:
...     myfile.write(line + '\n')
...
>>> myfile.seek(0)
>>> myfile.read()
'Hello World!\nThe total value = $1820.00\n'
>>> myfile.close()
>>> myfile = open("testit.txt", "r+a")
>>> myfile.read()
'Hello World!\nThe total value = $1820.00\n'
>>> for line in mydata:
...     myfile.write(line + '\n')
...
>>> myfile.seek(0)
>>> myfile.read()
'Hello World!\nThe total value = $1820.00\nHello World!\nThe total value = $1820.00\n'
>>> myfile.close()

要把數據寫入文件,必須先創建 file 對象。但是,在這情況下,必須用 'w' 模式標記指定要寫入文件。在這個示例中,把 mydata list 的內容寫入文件,關閉文件,然後重新打開文件,這樣就可以讀取內容了。

但是,通常情況下,想要同時讀取文件和寫入文件,所以這個示例的下一部分用 'r+' 模式重新打開文件。因爲能夠寫入文件,而不是添加,所以文件會被截斷。首先,把 mydata list 的內容寫入文件,然後把文件指針重新定位到文件開頭,並讀入內容。然後這個示例關閉文件,並用讀取和添加模式 "r+a" 重新打開文件。正如示例代碼所示,文件內容現在是兩個寫入操作的結果(文本是重複的)。

處理二進制數據

前面所有的示例都處理文本數據或字符數據:寫入和讀取字符 string。但是,在某些情況下,例如在處理整數或壓縮文件時,需要能夠讀取和寫入二進制數據。在創建 file 對象時,通過把 'b' 添加到文件模式中,可以很容易地用 Python 處理二進制數據,如清單 7 所示。

清單 7. 處理二進制數據
1
2
3
4
5
6
7
8
9
>>> myfile = open("testit.txt", "wb")
>>> for c in range(50, 70):
...     myfile.write(chr(c))
...
>>> myfile.close()
>>> myfile = open("testit.txt")
>>> myfile.read()
'23456789:;<=>?@ABCDE'
>>> myfile.close()

在這個示例中,創建一個合適的 file 對象,然後用從 50 到 69 的 ASCII 值寫入二進制字符。使用 chr 方法,把 range 方法調用創建的整數轉變成字符。在寫完所有數據之後,關閉文件並重新打開文件進行讀取,還是使用二進制模式標記。讀取文件可以證明沒有把整數寫入文件,相反,寫的是字符值。

在讀取和寫入二進制數據時,必須小心,因爲不同的平臺用不同的方式保存二進制數據。如果必須處理二進制數據,最好是使用來自 Python 庫的合適對象(或者來自第三方開發人員的對象)。

讀取和寫入:最有趣的地方

這篇文章討論了在 Python 程序中如何從文件讀取數據和寫入數據到文件中。總體來說,過程很簡單:創建合適的 file 對象,然後按照需要讀取和寫入。但是,在使用寫入模式創建 file 文件,向文件寫入數據時,必須注意文件的截斷。如果需要向文件中添加數據,應當在創建 file 對象時,使用添加模式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章