1. 程式人生 > >[Python]第十一章 檔案

[Python]第十一章 檔案

文章目錄


目前為止,程式與外部的互動很少,且都是通過input和print進行的。讓程式能夠與更大的外部世界互動:檔案和流。本章介紹的函式和物件讓你能夠永久儲存資料以及處理來自其他程式的資料。

11.1開啟檔案open()

open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
#Open file and return a stream.  Raise IOError upon failure.

用函式open,它位於自動匯入的模組io中.
file:
開啟當前目錄的檔案

>>> f = open('somefile.txt')

如果檔案位於其他地方,可指定完整的路徑。(注意路徑反斜槓/)

f=open('G:/Anaconda3/text.txt')
type(f)
_io.TextIOWrapper

mode:

描述
r 讀取模式(預設)
w 寫入模式:讓你能夠寫入檔案,並在檔案不存在時建立它。既有內容將被刪除(截斷)
x 獨佔寫入模式:更進一步,在檔案已存在時引發FileExistsError異常。
a 附加模式:相比較在寫入模式w下開啟檔案時,既有內容將被刪除(截斷)並從檔案開頭處開始寫入;附加模式可以在既有檔案末尾繼續寫入。
b 二進位制模式(與其他模式結合使用):如果檔案包含非文字的二進位制資料,如聲音剪輯片段或影象,不希望執行自動轉換。為此,只需使用二進位制模式(如’rb’)來禁用與文字相關的功能。
t 文字模式(預設,與其他模式結合使用)
+ 讀寫模式(與其他模式結合使用):可與其他任何模式結合起來使用,表示既可讀取也可寫入。例如,要開啟一個文字檔案進行讀寫,可使用’r+’。

預設模式為’rt’,把檔案視為經過編碼的Unicode文字,將自動執行解碼和且預設使用UTF-8編碼。
encoding和errors:
要指定其他編碼和Unicode錯誤處理策略,可使用關鍵字引數encoding和errors。
這還將自動轉換換行字元。預設情況下,行以’\n’結尾。讀取時將自動替換其他行尾字元(’\r’或’\r\n’);寫入時將’\n’替換為系統的預設行尾字元(os.linesep).
newline:
通常,Python使用通用換行模式。在這種模式下,後面將討論的readlines等方法能夠識別所有合法的換行符(’\n’、’\r’和’\r\n’)。如果要使用這種模式,同時禁止自動轉換,可將關鍵字引數newline設定為空字串,如open(name, newline=’’)。如果要指定只將’\r’或’\r\n’視為合法的行尾字元,可將引數newline設定為相應的行尾字元。這樣,讀取時不會對行尾字元進行轉換,但寫入時將把’\n’替換為指定的行尾字元。

11.2文字的基本方法

檔案物件的一些基本方法以及其他類似於檔案的物件(有時稱為流)。

11.2.1讀取和寫入(read\write)

#以寫入模式開啟:w
>>>f=open('file.txt','w')#檔案不存在的時候會建立的
>>>f.write('hello,')#返回寫入了多少個字元
6
>>>f.write('world')#只要不是在close之後再write,之前不會覆蓋
5
>>>f.close()#這時文字已經被寫入,開啟文件可以看見寫入的內容
text:hello,world

#以讀取模式開啟:r (預設)
>>>f=open('file.txt')
>>>f.read(4)
'hell'

#以附加模式開啟:a
#如果close後想重新寫入文件,則還是以’w’模式開啟(之前的內容將被清除)
#如果只想在後面追加,則以’a’附加模式開啟(之前的內容將被保留)
>>f=open('file.txt','a')
>>>f.write('\n你好')
3
>>>f.close()
text:   hello,world
		你好

#以讀寫模式開啟:r+
>>>f=open('file.txt','r+')
>>>f.write('\n世界')
3
>>>f.read(4)#如果有再次執行該語句,則會從之前獨到的地方往下讀,而不是從頭
'hell'#close之前只能讀取現存文件內容
>>>f.close()
text: hello,world
      你好
      世界

Notice:非讀模式下無法使用read(),非寫模式下無法使用write()

11.2.2使用管道重定向輸出

$ cat somefile.txt | python somescript.py | sort 
(Windows中:type somefile.txt | python somescript.py | sort )

管道字元(|)將一個命令的標準輸出連結到下一個命令的標準輸入。
這條管道線包含三個命令。

  • cat somefile.txt:將檔案somefile.txt的內容寫入到標準輸出(sys.stdout)。
  • python somescript.py:執行Python指令碼somescript。這個指令碼從其標準輸入中讀取,並將結果寫入到標準輸出。
  • sort:讀取標準輸入(sys.stdin)中的所有文字,將各行按字母順序排序,並將結果寫入到標準輸出。

somescript.py從其sys.stdin中讀取資料(這些資料是somefile.txt寫入的),並將結果寫入到其sys.stdout(sort將從這裡獲取資料)。

# somescript.py 
import sys
text=sys.stdin.read()
wordcount=len(text.split())
print(wordcount,':',text)
#type(sys.stdin)
#_io.TextIOWrapper
# somefile.txt
we are family!
#執行:
>>>type somefile.txt|python somescript.py
#輸出:
3 : we are family!

Notice:python somescript.py somefile.txt命令將會從控制檯輸入讀取內容,因為這個命令並沒有指定程式間的連結

EXTEND:隨機存取:seek()和tell()
seek找到指定位置

>>>em=open('emp.txt','w')
>>>em.write('0123456789')
>>>em.seek(5)#找到要寫入的位置
5
>>>em.write('****')#從找到的位置處開始重新寫入,而非從後面接著寫
>>>em.close()
>>>em=open('emp.txt')
>>>em.read()
'01234****9'

tell返回到達的位置

>>>em=open('emp.txt')
>>>em.read(3)
'012'
>>>em.read(2)
'34'
>>>em.tell()
5

11.2.3讀取和寫入行

要讀取一行(從當前位置到下一個分行符的文字),可使用方法readline。
要將所有行讀取,並以列表的形式返回,可以使用方法readlines。

f=open('cookie.txt')
#可以沒有傳參,預設讀取一行
f.readline()
'# Netscape HTTP Cookie File\n'
#傳入非負數,表示最多可以讀取個字元(接著上面讀)
f.readline(5)
'# htt'
#讀取檔案中的所有行,並以列表的方式返回它們,可使用方法readlines。(接著上面讀)
f.readlines()
['p://curl.haxx.se/rfc/cookie_spec.html\n',
 '# This is a generated file!  Do not edit.\n',
 '\n', '.baidu.com\tTRUE\t/\tFALSE\t3682520781\tBAIDUID\tE539BADFFA31FDF49E09921ACDA3EDFE:FG=1\n',
'.baidu.com\tTRUE\t/\tFALSE\t3682520781\tBIDUPSID\tE539BADFFA31FDF49E09921ACDA3EDFE\n',
 '.baidu.com\tTRUE\t/\tFALSE\t\tH_PS_PSSID\t1448_21117_26350_26922_20927\n',
 '.baidu.com\tTRUE\t/\tFALSE\t3682520781\tPSTM\t1535037133\n',
 'www.baidu.com\tFALSE\t/\tFALSE\t\tBDSVRTM\t0\n',
 'www.baidu.com\tFALSE\t/\tFALSE\t\tBD_HOME\t0\n',
 'www.baidu.com\tFALSE\t/\tFALSE\t2481117075\tdelPer\t0\n']
  • read():讀取整個文件,以字串格式輸出
  • readline():讀取單行文件,以字串格式輸出
  • readlines():讀取文件所有行,每行為一個元素,以列表格式輸出

方法writelines與readlines相反:接受一個字串列表(實際上,可以是任何序列或可迭代物件),並將這些字串都寫入到檔案(或流)中。
寫入時不會新增換行符,因此你必須自行新增。

ff=open('file.txt','a')
ff.writelines('hello\n world')
ff.close()
text: hello
      world

沒有writeline,因為可以使用write.

11.2.4關閉檔案

要確保檔案得以關閉,可使用一條try/finally語句,並在finally子句中呼叫close。

# 在這裡開啟檔案
try: 
  # 將資料寫入到檔案中
finally: 
  file.close()

實際上,有一條專門為此設計的語句,那就是with語句。

with open("somefile.txt") as somefile: 
	do_something(somefile) 

該語句允許使用上下文管理器
CASE:簡化路徑輸入
如果每次檢視檔案不方便拷貝,需要輸入很長的路徑
$ less D:\ProgramData\Anaconda3\Lib\sqlite3\test\afile
可以寫讀取文件的簡單程式碼

#refi.py
import sys
def main(file_nane):
	if file_name=='afile':
	dictionary='D:\ProgramData\Anaconda3\Lib\sqlite3\test\afile'

	with open(dictionary) as f:
		print(f.read()
if __name__=='__main__':
	main(sys.argv[1])

每次這樣呼叫即可:
$ python refi.py afile|less

11.2.5使用檔案的基本方法

read() readline() readlines() write() writelines()

#write() 
>>>f=open('emp.txt','w')
>>>f.write('hello,\nworld')
>>>f.close()
text: hello,
      world

#read() 
>>>f=open('emp.txt')
>>>print(f.read())
hello,
world
>>>f.close()

#readline()
>>>f=open('emp.txt')
>>>for i in range(2):
...    print(i,f.readline())
>>>f.close()
0 hello,

1 world

#readlines()
>>>f=open('emp.txt')
>>>print(f.readlines())
['hello,\n', 'world']
>>>f.close()

#writelines()
>>>f=open('emp.txt','r+')
>>>lines=f.readlines()#這裡沒有close後重新開啟,導致原來的文件被保留,下文以追加的方式寫入,如果這裡close後重新open,則以覆蓋的方式寫入
>>>lines[0]='\nWelcome,\n'
>>>f.writelines(lines)
>>>f.close()
text:
	hello,
	world
	Welcome,
	world

Notice:open一個file後,執行read,並把read的內容賦值給一個變數,再重新把變數write,最後才close,這時寫入模式會變成追加模式。

11.3迭代檔案內容

之前使用函式執行一次出一次結果,例如讀取一行,就要執行一次readline(),如果需要逐行讀取,需要用到迭代。將使用一個名為process的虛構函式來表示對每個字元或行所做的處理

def process(string): 
	print('Processing:', string) 

11.3.1 每次一個字元(或位元組)

def process(string):
    print('processing:',string)

with open('emp.txt') as f:
    char=f.read(1)
    while char:
        process(char)
        char=f.read(1)

改進

def process(string):
    print('processing:',string)

with open('emp.txt') as f:    
    while True:
        char=f.read(1)
        if not char:break
        process(char)
processing: h
processing: e
processing: l
processing: l
processing: o

這裡如果有換行符,也會被當做一個字元打印出來,只不是顯示為空

11.3.2每次一行

def process(string):
    print('processing:',string)

with open('emp.txt') as f:    
    while True:
        char=f.readline()
        if not char:break
        process(char)
processing: hello,

processing: world

processing: Welcome,

processing: world
#中間的空行實際上是句尾的換行符

11.3.4使用fileinput實現延遲迭代

實現逐行讀取,避免使用readlines()太佔記憶體,可以使用while和readline()組合,更優先使用的是for迴圈,這裡用到fileinput.input(filename)

def process(string):
    print('processing:',string)
import fileinput
for line in fileinput.input('file.txt'):
	process(line) 
fileinput.close()

輸出結果和上一節是一樣的。
Notice:沒有fileinput.close(),可能會報錯:input() already active

11.3.5檔案迭代器

其實檔案本身就可以迭代

def process(string):
    print('processing:',string)

with open('file.txt') as f:    
    for line in f:#流本身就可以迭代
        process(line)
processing: you are my

processing:  frienshello

processing:  world

processing: hello

如果不寫入檔案可以不關閉檔案,可以不用上下文管理器with語句

def process(string):
    print('processing:',string)
for line in open('file.txt'):
        process(line)

sys.stdin也是可迭代的

def process(string):
    print('processing:',string)
    
import sys 
for line in sys.stdin: 
	process(line) 

可對迭代器做的事情基本上都可對檔案做

>>>list(fileinput.input('emp.txt'))
>>>list(open('emp.txt'))
['hello,\n', 'world\n', 'Welcome,\n', 'world']

CASE 對檔案序列解包

>>>f=open('emp.txt','w')
>>>print('first',file=f)#這裡不會直接打印出來,而是會寫入file
>>>print('second',file=f)
>>>print('finally',file=f)
>>>f.close()
>>>lines=list(open('emp.txt'))
>>>lines
['first\n', 'second\n', 'finally\n']
>>>first,second,third=open('emp.txt')#序列解包
>>>first
'first\n'
text:
	first
	second
	finally

Notice:
print()其實有file這個引數
print(…)
print(value, …, sep=’ ‘, end=’\n’, file=sys.stdout, flush=False)

Prints the values to a stream, or to sys.stdout by default.
Optional keyword arguments:
file:  a file-like object (stream); defaults to the current sys.stdout.
sep:   string inserted between values, default a space.
end:   string appended after the last value, default a newline.
flush: whether to forcibly flush the stream.

序列解包,變數儲存first,second,third=open(‘emp.txt’)
寫入檔案後要將其關閉

CASE:處理文件

import sys,os

def convertline(line):
	new_line=line.replace('\n','').replace('\t','')
	linelist=new_line.split(' ')
	new_linelist=[]
	for word in linelist:
		if len(word)!=0:
			new_linelist.append(word)
	return ' '.join(new_linelist)+' '
	
	
	
def main(deal):
	#setup files
	ticker,series=deal.split('_')
	scriptpath=os.getcwd()
	path_to_text=os.path.join(scriptpath,ticker,series)
	
	int_file=path_to_text+'/'+'int.txt'
	prin_file=path_to_text+'/'+'prin.txt'
	eod_file=path_to_text+'/'+'eod.txt'
	
	files=[int_file,prin_file,eod_file]
	#write the files
	
	for file in files:
		sl=[]
		for line in open(file):
			line=line.strip()
			if len(line)!=0:
				sl.append(convertline(line))	
		with open(file,'w') as w:
			w.writelines(sl)



if __name__=='__main__':
	main(sys.argv[1])