Python學習5
1.檔案讀寫
(1)讀檔案
使用open()
傳入檔案和識別符號:
f = open('D:/Workspace/PycharmProjects/test/beread.txt', 'r')//識別符號'r'表示讀
//如果檔案不存在,就會丟擲IOError錯誤,如果開啟成功,就可以用read()方法一次把檔案的全部內容讀到記憶體
f.read()
//最後需要呼叫close()關閉檔案
f.close()
如果檔案讀寫過程中產生IOError
,後面的f.close()
就不會呼叫,檔案就無法關閉,解決這個問題的第一種方法是使用try...finally
:
try:
f = open('/path/to/file' , 'r')
print f.read()
finally:
if f:
f.close()
這麼寫比較繁瑣,所以Python引入了with
語句來自動呼叫close
方法:
with open('/path/to/file', 'r') as f:
print f.read()
read()
方法會一次性讀取檔案的全部內容,檔案比較小時,這種讀取方法最方便;如果不能確定檔案大小,反覆呼叫read(size)
比較保險,size
表示每次最多讀取size個位元組的內容;如果是配置檔案,呼叫readlines()
最方便:
for line in f.readlines():
print(line .strip()) //去掉末尾的'ln'
(2)file-like Object
像open()
函式返回的這種有個read()
方法的物件,在Python中統稱為file-like Object。除了file外,還可以是記憶體的位元組流,網路流,自定義流等等。file-like Object不要求從特定類繼承,只要寫個read()
方法就行。
(3)讀取二進位制檔案&非ASCII編碼檔案
前面講的預設都是讀取文字檔案,並且是ASCII編碼的文字檔案。要讀取二進位制檔案,比如圖片,視訊等等,要用識別符號'rb'
開啟:
open('D:/Workspace/PycharmProjects/test/beread.jpg' , 'rb')
如果要讀非ASCII編碼的檔案,就必須以二進位制模式開啟,再解碼。比如GBK編碼的檔案:
f = open('D:/Workspace/PycharmProjects/test/gbk.txt', 'rb')
u = f.read().decode('gbk')
這樣子手動裝換太麻煩,Python提供了一個codecs模組來在讀檔案時自動轉換編碼,直接讀出unicode:
import codecs
with codecs.open('D:/Workspace/PycharmProjects/test/gbk.txt', 'r', 'gbk') as f:
f.read()
(4)寫檔案
使用write()
函式,方法和讀檔案類似,只是識別符號要改為'w'
或者'wb'
with open('/Users/michael/test.txt', 'w') as f:
f.write('Hello, world!')
2.操作檔案和目錄
Python內建的os
模組可以直接呼叫作業系統提供的介面函式。操作檔案和睦的函式一部分放在os
模組中,一部分放在os.path
模組中:
os.path.abspath('.') //檢視當前目錄的絕對路徑
D:\Workspace\PycharmProjects\test
#在該目錄下建立一個新目錄
#首先把新目錄的完整路徑表示出來
os.path.join('D:\Workspace\PycharmProjects\test', 'newcreate')
#然後建立一個新的目錄:
os.mkdir('D:\Workspace\PycharmProjects\test\newcreate')
#刪掉一個目錄
os.rmdir('D:\Workspace\PycharmProjects\test\newcreate')
把兩個路徑合成一個時,不要直接拼字串,而要通過os.path.join()
,這樣可以正確處理不同作業系統的分隔符,拆分時也是同樣道理,要用os.path.split()
,拆分後的後一部分總是最後級別的目錄或檔名
os.path.split('D:\Workspace\PycharmProjects\test\newcreate')
('D:\Workspace\PycharmProjects\test','newcreate')
os.path.splitext()
可以直接得到副檔名
os.path.splitext('/path/to/file.txt')
('/path/to/file', '.txt')
對當前目錄下檔案的操作:
os.rename('beread.txt', 'newname.py')//檔案重新命名
os.remove('beread.jpg')//刪除檔案
但是os
模組中沒有複製檔案的函式,但在shutil
模組中提供了copyfile()
的函式,傳入兩個引數,第一個是被複制檔案的名字,第二個是新檔案的名字。
os.listdir('.') # 列出當前目錄的所有檔案和子目錄
os.path.isdir(x) # 判斷x是否為目錄
os.path.isfile(x) # 判斷x是否為檔案
os.walk('.') # 遍歷當前目錄的所有檔案和子目錄和子目錄的檔案,返回值去pycharn裡面看
3.序列化
①Python提供cPickle
和pickle
兩個模組來實現序列化,兩個模組功能一樣,只是前者是用c語言寫的,速度快,所以匯入的時候先嚐試匯入前者:
try:
import cPickle as pickle
except ImportError:
import pickle
d = dict(name = 'Bob', age = 20, score = 88)
pickle.dumps(d)
pickle.dumps()
方法把任意物件序列化成一個str,然後就可以把這個str寫入檔案,或者用另一個方法pickle.dump()
直接把物件序列化後寫入一個file-like Object:
f = open('dump.txt', 'wb')
pickle.dum(d, f)
f.close()
#反序列化
f = open('dump.txt', 'rb')
d = pickle.load(f)
f.close()
print d
{'age': 20, 'score': 88, 'name': 'Bob'}
②JSON
如果我們要在不同的程式語言之間傳遞物件,就必須把物件序列化為標準格式,比如XML,但更好的方法是序列化為JSON,因為JSON表示出來就是一個字串,可以被所有語言讀取,也可以方便地儲存到磁碟或者通過網路傳輸。JSON不僅是標準格式,並且比XML更快,而且可以直接在Web頁面中讀取,非常方便。
Python的json
模組提供了轉換方法:
import json
d = dict(name='Bob', age=20, score=88)
json.dumps(d) # dumps()方法返回一個內容為JSON標準的str
f = open('jsondump.txt', 'wb')
json.dump(d, f) # dump()方法把d轉化為JSON後寫入一個file-like Object.
f.close()
JSON同樣有loads()
和load()
的方法來進行反序列化,loads()
把JSON型別的字串反序列化,load()
從file-like Object讀取字串並且反序列化,需要注意的是反序列化得到的所有字串物件預設都是unicode
而不是str
:
import json
f = open('dump.txt', 'rb')
d = json.load(f)
f.close()
print d
{u'age': 20, u'score': 88, u'name': u'Bob'}
③JSON進階
class Student(object):
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
s = Student('Bob', 20, 88)
print(json.dumps(s))
當直接用dumps(d)
序列化一個class
物件d時會報錯,因為class
物件不是一個可序列化為JSON的物件,不過dumps()
方法還有很多可選引數,可選引數default
就是把任意一個物件變成一個可序列為JSON的物件,我們只需要為類物件寫一個轉換函式,再把函式傳入:
def change(std):
return {
'name': std.name
'age': std.age
'score': std.score
}
print(json.dumps(s, default = change))
另一種方法是通過class
例項的__dict__
屬性,它就是一個dict
,用來儲存例項變數。也有少數例外,比如定義了__slots__
的class
。
print(json.dumps(s, default=lambda obj: obj.__dict__))
反序列化的時候,同樣要寫一個函式傳入loads()
的object_hook
,把dict
物件轉化了類的例項:
def changeback(d):
return Student(d['name'], d['age'], d['score'])
json_str = '{"age": 20, "score": 88, "name": "Bob"}'
print(json.loads(json_str, object_hook = changeback))
4.正則表示式
①單子字元匹配
\d
匹配一個數字,\w
匹配一個字母或者數字,.
匹配任意字元,\s
匹配空格
②變長字元匹配
*
表示任意個字元,包括0,
+
表示至少一個字元,
?
表示0個或者一個字元,
用{n}
表示n個字元,
用{m,n}
表示m到n個字元
③'-'
這樣的特殊字元要用'\'
轉義
④進階:
要做更精確的匹配,可以用[]
表示範圍,比如:
[0-9a-zA-Z\_]
可以匹配一個數字、字母或者下劃線;
[0-9a-zA-Z\_]+
可以匹配至少由一個數字、字母或者下劃線組成的字串,比如'a100'
,'0_Z'
,'Py3000
‘等等;
[a-zA-Z\_][0-9a-zA-Z\_]*
可以匹配由字母或下劃線開頭,後接任意個由一個數字、字母或者下劃線組成的字串,也就是Python合法的變數;
[a-zA-Z\_][0-9a-zA-Z\_]{0, 19}
更精確地限制了變數的長度是1-20個字元(前面1個字元+後面最多19個字元)。
A|B
可以匹配A或B,所以(P|p)ython
可以匹配'Python'
或者'python'
。
^
表示行的開頭,^\d
表示必須以數字開頭。
$
表示行的結束,\d$
表示必須以數字結束。
⑤re模組
Python提供re
模組,包含所有正則表示式的功能,匹配使盡量使用r''
,這樣就不需要轉義:
import re
print re.match(r'\d{3}\s\-\s\d{3,8}', '010 - 12345')
<_sre.SRE_Match object at 0x0266EC28>
print re.match(r'\d{3}\-\d{3,8}', '010 - 12345')
None
如果match()
匹配成功,則返回一個Match
物件,否則返回None
⑥切分字串
用re.split(s)
切分字串比普通的`s.split(’ ‘)功能更強大,就算有連續的空格或者其他符號,都能切分出來:
re.split(r'[\s\,\;]+', 'a,b;; c d')
['a', 'b', 'c', 'd']
⑦分組
除了簡單地判斷是否匹配之外,正則表示式還有提取子串的強大功能。用()
表示的就是要提取的分組,比如^(\d{3})-(\d{3,8}$)
就定義了兩個組,可以直接從匹配的字串中提取出區號和本地號碼:
m = re.match(r'^(\d{3})-(\d{3,8})$', '010-12345')
print m.group(0) # group(0)永遠是原始字串
'010-12345'
print m.group(1), m.group(2) # group(1), group(2), ....表示第1、2、...個子串
'010' '12345'
print m.groups() # 打印出所有子串
('010', '12345')
⑧貪婪匹配
正則匹配預設是貪婪匹配,會盡可能多的匹配字元:
re.match(r'^(\d+)(0*)', '102300').groups()
('102300', '')
由於\d+
採用貪婪匹配,所以會把整個字串全部匹配掉,所以後面的0*
只能匹配空字串,要想\d+
採用非貪婪匹配,在其後面加個?
就可以:
re.match(r'^(\d+?)(0*)', '102300').groups()
('1023', '00')
⑨編譯
當我們在Python中使用正則表示式時,re模組內部會幹兩件事情:
1.編譯正則表示式,如果正則表示式的字串本身不合法,會報錯;
2.用編譯後的正則表示式去匹配字串。
如果一個正則表示式要重複使用幾千次,出於效率的考慮,我們可以預編譯該正則表示式,接下來重複使用時就不需要編譯這個步驟了,直接匹配:
import re
re_pre = re.compile(r'^(\d{3})-(\d{3,8})$') #預編譯
re_pre.match('010-12345').groups()
('010', '12345')