1. 程式人生 > >Python學習5

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提供cPicklepickle兩個模組來實現序列化,兩個模組功能一樣,只是前者是用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')