文本是軟件工程師日常工作中處理最多的數(shù)據(jù)類型,幾乎無時無刻不在與文本打交道。Linux下有很多的文本處理工具,但是有很多的高級特性都依賴于正則表達(dá)式,學(xué)習(xí)曲線較為陡峭。
Python語言內(nèi)嵌的字符類型包含大量的文本處理函數(shù),Python的標(biāo)準(zhǔn)庫對文本處理提供了很好的支持。所以,我們也能使用Python來處理文本的需求。
4.1 字符串常量
4.1.1 定義字符串
在Python中,所有的字符都是字符串。因?yàn)镻ython不區(qū)分字符和字符串,所以Python可以使用單引號或者雙引號來定義字符串。如: name = "super man"。
在遇到特殊字符的時候,我們需要轉(zhuǎn)義字符""對字符進(jìn)行轉(zhuǎn)移。場景的轉(zhuǎn)移字符有
| 轉(zhuǎn)義字符 | 含義 | 轉(zhuǎn)義字符 | 含義 |
|---|---|---|---|
| \n | 換行 | \t | 水平制表 |
| \r | 回車 | \|代表一個反斜線字符'' |
如我們在處理windows的路徑的時候就需要轉(zhuǎn)義字符的幫忙了
#這個會報(bào)錯的
path= "c:\next"
#這樣才行
path="c:\\next"
除了使用轉(zhuǎn)義字符以外,還可以使用原始字符串(raw string)。原始自負(fù)床的使用非常簡單,就是在字符串定義前面加上一個"r",如: r"Hello world"。原始字符串會抑制所有的轉(zhuǎn)移,打印字符中的所有反斜杠。所以上面的例子也可以這樣寫:
path=r"c:\\next"
在Python中,如果你定義的字符串較長,可以使用三引號來定義字符串。使用三引號定義的字符串一般稱為多行字符串。多行字符串不受代碼縮進(jìn)規(guī)則的限制,因?yàn)樗旧砭筒皇谴a,而是字符串,此外也不會被轉(zhuǎn)義,
Python字符串還有一個容易被忽略的小特性,就是兩個字符串會自動主從一個新的字符串。如下:
#s的結(jié)果是 helloworld
s="hello" "world"
4.1.2 字符串是不可變的有序集合
Python的字符串有兩個特點(diǎn): 1是不可變的,2是有序的集合。
由于Python字符串是不可變的,所以不能直接對字符串進(jìn)行修改。但是可以通過字符運(yùn)算、切片操作、格式化表達(dá)式和字符串方法調(diào)用等方式創(chuàng)建新的字符串。然后把結(jié)果賦值給最初的變量名,達(dá)到修改字符串的目的。此外,正式因?yàn)椴豢勺兊奶匦?。所以對字符串進(jìn)行操作的時候都會產(chǎn)生一個新的字符串,新的字符串會占用一塊獨(dú)立的內(nèi)存。因此,操作字符串時需要避免產(chǎn)生太多的中間結(jié)果。下面的反面例子:
fruits = ['apple1','apple2','apple3','apple4']
statement = fruits[0]
for item in fruits[1:]:
statement = statement + ', ' + item
print(statement)
在這個例子中,產(chǎn)生了大量的中間結(jié)果。產(chǎn)生了臨時字符串,它們已產(chǎn)生就被銷毀了,白白浪費(fèi)了程序的運(yùn)行時間。示例的正確做法是使用join方法,如下:
', '.join(fruits)
Python字符串的第二個特點(diǎn)就是通過下標(biāo)和切片進(jìn)行訪問。在Python語言中,元組、列表和字符串都是元素的有序集合,都可以使用下標(biāo)和切片進(jìn)行訪問。
4.1.3 字符串函數(shù)
Python提供與字符串處理相關(guān)的方法可以分為兩大類。一類是可以用于多種類型的通用操作,以內(nèi)置函數(shù)或表達(dá)式的方式提供。如len(s)、'x' in s等。另一類是只作用于字符串的特定類型操作,以方法調(diào)用的形式提供,如str.split()和str.upper()等。
通用操作
如 len('pcm')、 'c' in 'pcm'與大小寫相關(guān)的方法
- upper: 轉(zhuǎn)為大寫
- lower: 轉(zhuǎn)為小寫
- isupper: 判斷是否為大寫
- islower: 判斷是否為小寫
- swapcase: 大寫轉(zhuǎn)小寫,小寫轉(zhuǎn)大寫
- capitalize: 將首字母轉(zhuǎn)換為大寫
- istitle: 判斷字符串是不是標(biāo)題
- 判斷類方法
除了前面介紹的幾個is開頭的方法,還有下面的這些也是很常見的:
- isalpha: 是否只包含字母
- isalnum: 是否只包含字符串字母和數(shù)字
- isspace: 是否只包含空格、制表符、換行符
- isdecimal: 是否自包含數(shù)字字符
- 字符串方法startswith和endswith
這兩個判斷類方法用來判斷字符串的前綴或后綴。下面示例一個常見的場景:統(tǒng)計(jì)出MongoDB日志文件占用磁盤的大小。
import os
mongod_logs = [item for item in os.listdir('/var/mongo/log') if item.startswith('mongod.log')]
sum_size=sum(os.path.getsize(os.path.join('/var/mongo/log'),item)) for item in mongod_logs)
- 查找類函數(shù)
- find:查找子串在字符串中的位置,如果查找失敗,返回-1
- index: 與find函數(shù)類似,如果查找失敗,拋出ValueError異常
- rfind: 與find函數(shù)類似,區(qū)別在于rindex是從后向前查找
- rindex: 與index函數(shù)類似,區(qū)別在于rindex是從后向前查找
這幾個函數(shù)的用法和作用都差不多,這里介紹find的用法
s = 'wo you yi tou xiao mao lv.'
s.find('yi')
- 字符串操作方法
字符串的join函數(shù)用來連接字符串列表,組成一個新的、更大的字符串。join是一個字符串處理函數(shù),需要先有字符串,再調(diào)用這個函數(shù)。因此,如果僅僅需要將幾個字符串連接起來,并且不需要插入任何額外的字符,則可以使用空字符串調(diào)用join方法,如下所示:
#結(jié)果為abc
"".join(['a','b','c'])
#結(jié)果為a,b,c
",".join(['a','b','c'])
join函數(shù)比前面介紹的更加通用,join函數(shù)只有一個參數(shù),并且是iterable而不是列表。也就是說,join接受任何可迭代的對象。因此,如果我們需要將文件中的內(nèi)容拼接起來組成一個更大的字符串,我們自需要將文件對象傳遞給join函數(shù)即可,因?yàn)槲募ο蟊旧砭褪且粋€可迭代的對象。
with open('/etc/passwd') as f:
print("###".join(f))
join函數(shù)最容易被濫用的地方是打印字符串列表時,print函數(shù)本身可以通過sep參數(shù)指定分隔符:
print('root','/root','/bin/bash',seq=':')
如果使用join函數(shù)先組成字符串然后再打印,就很容易出錯,并且性能也變差。如join的列表中存在數(shù)字,join函數(shù)不會自動將數(shù)字轉(zhuǎn)換為字符串,然后會拋出異常信息,這樣就不能做打印處理了。
接下來看一下與join函數(shù)起反作用的split函數(shù)。join函數(shù)用以將字符串列表(更準(zhǔn)確地說,是可迭代對象)拼接成更大的字符串,而split函數(shù)正好相反,它用以將一個字符串拆分成一個字符串列表。
split默認(rèn)使用空白字符作為分隔符,當(dāng)然也可以指定分隔符。如下:
'super:man'.split(':')
strip、rstrip和lstrip這幾個函數(shù)用來多字符串進(jìn)行剪裁,最常用的場景就是去除兩端的空白字符。當(dāng)然也可以傳遞參數(shù),去除特定的字符,如下:
"##Hello,world####.strip('#')"
replace函數(shù)非常簡單,顧名思義就是將字符串中的子串替換成一個新的子串。
4.1.4 案例:使用Python分析Apache的訪問日志
下面的代碼可以統(tǒng)計(jì)網(wǎng)站訪問的PV和UV。
#!/usr/bin/python
from __future__ import print_function
ips = []
with open('access.log') as f:
for line in f:
ips.append(line.split()[0])
print("PV is {0}".format(len(ips)))
print("UV is {0}".format(len(set(ips))))
下面我們接觸collections.Counter保存資源的熱度,Count是dict的子類,使用方式與字典類似。對于普通的計(jì)數(shù)功能,Count比字典更加好用。如下所示:
#變量c的結(jié)果是 Count({'a':2,'b':2,'c':1})
c=Counter('abcba')
Counter作為一個計(jì)數(shù)器,還提供了一個名為most_common的函數(shù),用來顯示Counter中取值最大的幾個元素。下面的代碼使用Counter統(tǒng)計(jì)網(wǎng)站中最熱門的十項(xiàng)資源:
#!/usr/bin/python
#-*- coding: UTF-8 -*-
from __future__ import print_function
from collections import Counter
c = Counter()
with open('access.log') as f:
for line in f:
c[line.split()[6]] += 1
print("Popular resources : {0}".format(c.most_common(10)))
4.1.5 字符串格式化
在Python中,存在兩種格式化字符串的方法,即%表達(dá)式和format函數(shù)。%表達(dá)式從Python誕生之日就開始存在了,是基于C語言的printf模型,目前還廣泛使用。format函數(shù)是Python2.6和Python3.0新增加的技術(shù),是Python獨(dú)有的方法,并且和字符串格式表達(dá)式的功能有不少的重疊。雖然%表達(dá)式目前廣泛使用,但是,format函數(shù)才是字符串格式化的未來,%表達(dá)式在Python未來的版本可能會棄用。
最簡單的format函數(shù)使用應(yīng)該是通過參數(shù)的位置訪問參數(shù)。如下所示,通過{}來表示一個占位符,Python會自動將format函數(shù)的參數(shù)依次傳遞給{}占位符。
#需要按順序來填寫參數(shù)
"{} is better than {}.".format('A','B')
#通過下標(biāo)來訪問
"{0} is better than {1}.".format('A','B')
4.2 正則表達(dá)式
正則表達(dá)式是個處理文本的強(qiáng)大工具,在文本處理程序中廣泛使用,如Offic world、vim等。正則表達(dá)式在Linux命令行中使用更加廣泛,大部分文本處理工具都支持正則表達(dá)式,如grep、awk、sed等。正則表達(dá)式,最大的問題就是學(xué)習(xí)的難度較大,很容易學(xué)完就忘了。
4.2.1 正則表達(dá)式語法
正則表達(dá)式由普通文本和具有特殊意義的符號組成,工程師根據(jù)具體的需要,使用它們構(gòu)造出合適的正則表達(dá)式來匹配文本。例如:
- 要匹配給定文本中的所有單詞
#"?"用于匹配單詞前后可能出現(xiàn)的空格,[a-zA-Z]+代表一個或多個英文字母
?[a-zA-Z]+
- 要匹配一個IP地址,可以使用下面的正則表達(dá)式:
[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}
下表給出正則表達(dá)式的基本組成部分
| 正則表達(dá)式 | 描述 | 示例 |
|---|---|---|
| ^ | 行起標(biāo)識記 | ^imap匹配以imap起始的行 |
| $ | 行尾標(biāo)記 | import$匹配以import結(jié)尾的行 |
| . | 匹配任意一個字符 | 它只能匹配單個字符,但是可以匹配任意字符,如linu.,可以匹配到linux與linus |
| [] | 匹配包含在[字符]之中的任意字符 | coo[kl]能夠匹配cook或cool |
| [^] | 匹配包含在^字符]之外的任意字符 | 9[^01]可以匹配92、93,但是不匹配91或90 |
| [-] | 匹配[]中指定范圍內(nèi)的任意一個字符 | [1-5]匹配1-5的任意一個數(shù)字,[a-z]匹配任意一個小寫字母 |
| ? | 匹配之前項(xiàng)1次或0次 | hel?o匹配hello或helo,但是不能匹配helllo |
| + | 匹配之前項(xiàng)1次或多次 | hel+匹配hel或hell,但是不能匹配he |
| * | 匹配之前項(xiàng)0次或多次 | hel*匹配he、hel、hell |
| {n} | 匹配之前的項(xiàng)n次 | [0-9]{3}匹配任意一個三位數(shù) |
| {n,} | 之前的項(xiàng)至少匹配n次 | [0-9]{3,}匹配任意一個三位數(shù)或更多的數(shù)字 |
| {n,m} | 指定之前的項(xiàng)所必須匹配的最小次數(shù)和最大次數(shù) | [0-9]{2,5}匹配從兩位數(shù)到五位數(shù)之前的任意一個數(shù)字} |
4.2.2 利用re庫處理正則表達(dá)式
正則表達(dá)式雖然本身比較負(fù)暫,但是,在Python中使用正則表達(dá)式卻出奇地簡單。在Python中,標(biāo)準(zhǔn)庫re模塊用來處理正則表達(dá)式,它能夠順利處理Unicode和普通字符串。這個模塊包含了與正則表達(dá)式相關(guān)的函數(shù)、標(biāo)志和一個異常。
下面的示例使用re模塊下的findall函數(shù)來匹配符合并輸出符合模式的子串
import re
data = "waht is the difference between python 2.7.13 and Python 3.6.0 ?"
re.findall('python [0-9]\.[0-9]\.[0-9]',data)
如果希望re模塊在模式匹配的時候忽略字符的大小寫,可以通過傳遞標(biāo)志的形式告訴re模塊忽略大小寫這個需求,如:
re.findall('python [0-9]\.[0-9]\.[0-9]',data,flags=re.IGNORECASE)
除了直接使用re模塊中的函數(shù)之外,還有另外一種使用正則表達(dá)式的方法,那就是創(chuàng)建一個特定模式編譯的正則表達(dá)式對象,然后使用這個對象中方法。
什么事編譯的正則表達(dá)式呢?它是一個簡單的對象,通過傳遞模式給re.compile函數(shù)創(chuàng)建。編譯與非編譯方式使用正則表達(dá)式的區(qū)別,除了使用方法之外,主要是性能方面的差異。編譯的性能明顯好于非編譯的。
import re
data = "waht is the difference between python 2.7.13 and Python 3.6.0 ?"
re_obj = re.compile('python [0-9]\.[0-9]\.[0-9]')
re_obj.findall(data)
4.2.3 常用的re方法
- 匹配類的函數(shù)
re模塊中最簡單的辨識findall函數(shù),該函數(shù)在字符串中查找模式匹配,將所有的匹配字符串以列表的形式返回。如果文本中沒有任何字符串匹配模式,則返回一個空的列表。如果有一個子串匹配模式,則返回一個元素的列表。所以,不管怎么匹配,都不會出錯。這對工程師編寫程序來說,減少異常情況的處理,代碼邏輯更加整潔。
match函數(shù)類似于字符串中startswith函數(shù),只是match更強(qiáng)大。match函數(shù)用以匹配字符串的開始部分,如果匹配成功,返回一個SRE_Match類型的對象,如果失敗,則返回一個None。例如,我們判斷data字符串是否以"What"和"Not What"開頭:
re.match('What',data)
re.match('Not What',data)
下面示例匹配一個字符串是否以數(shù)字開頭:
re.match('\d+',"123 is one hundred and twenty-three")
re模塊中的search函數(shù)模式匹配成功時,也會返回一個SRE_Match對象。其與match函數(shù)的用法幾乎一樣,區(qū)別在于前者在字符串的任意位置進(jìn)行匹配,后綴僅對字符串開始部分進(jìn)行匹配。要注意的是search僅僅查找第一次匹配,如果要返回多個匹配的結(jié)果,最簡單的做法是使用findall函數(shù)。
- 修改類函數(shù)
re模塊的sub函數(shù)類似于字符串的replace函數(shù),只是sub函數(shù)支持使用正則表達(dá)式,所以更加強(qiáng)大。例如,下面的正則表達(dá)式模式,可以同時匹配"2.7.13"和"3.6.0",并將他們都替換為"x.x.x"。
import re
data = "waht is the difference between python 2.7.13 and Python 3.6.0 ?"
re.sub('[0-9]+\.[0-9]+\.[0-9]+','x.x.x',data)
re模塊的split函數(shù)與Python字符串的split函數(shù)功能一樣,都是將一個字符串拆分為子串的列表,只是re模塊的能夠使用正則表達(dá)式。例如,對于下面這一段包含了冒號,逗號,單引號和若干空格的文本,我們希望拆分出每一個單次。
text = "MySQL slave binlog position: master host '10.1.1.1',filename 'mysql-bin.000002',position '524994060'"
re.split(r"[':,\s]+",text.strip("'"))
大小寫不敏感
在re模塊中,要忽略大小寫的差異,就如同前面的示例那樣,使用flags=re.IGNORECASE。非貪婪模式
在正則表達(dá)式的字符串匹配中,有貪婪匹配和非貪婪匹配的區(qū)別。貪婪匹配總是匹配到最長的那個字符串,非貪婪模式正好相反。例如:我們要匹配以"Beautiful"開頭并且以點(diǎn)好結(jié)尾的字符串,默認(rèn)情況下正則表達(dá)式使用貪婪匹配,如果要使用非貪婪匹配,只需要在匹配字符串時加上一個"?"。如下所示:
text = "Beautiful is better than ugly.Explicit is better than implicit."
re.findall('Beautiful.*\.',text)
re.findall('Beautiful.*?\.',text)
4.3 字符集編碼
在Python編程中,如果不使用Unicode,處理中文時將會遇到一些令人困惑的地方。需要注意的是,Python2.7默認(rèn)使用的不是Unicode,Python3默認(rèn)使用的是Unicode。
In [18]: name='超人'
In [19]: print(name)
超人
In [20]: print(name[0:1])
#輸出沒有結(jié)果
In [21]: name=u'超人'
In [22]: print(name)
超人
In [23]: print(name[0:1])
超
上面的示例可以看到使用中文的話,使用分片的時候得不到我們想要的結(jié)果。解決的方法也很簡單,只要在前面加個"u"來定義Unicode字符串就行了。
4.3.4 Python2和Python3中的Unicode
前面說到Python2中如果要使用Unicode編碼,則必須在字符串前面顯式地加上一個"u"前綴。其實(shí),Python2也可以默認(rèn)使用Unicode的字符串的,只需要執(zhí)行下面的導(dǎo)入即可:
from __future__ import unicode_literals
Python的字符串具有encode方法和decode方法。我們可以使用這兩個方法對字符串進(jìn)行編碼或者解碼,下面是一個在Python2下運(yùn)行的例子:
name='超人'
new_name=name.decode('utf8')
#能正常輸出"超"
print(new_name[:1])
#使用encode進(jìn)行編碼
new_name.encode('utf-8')
new_name.encode('utf-16')
我們既然已經(jīng)知道使用encode對Unicode進(jìn)行編碼,使用decode對字符進(jìn)行解碼,那么,如果我們要存儲中文的話,可以這樣操作:
name=u'超人'
#使用encode進(jìn)行編碼
with open('/tmp/data.txt', 'w') as f:
f.write(name.encode('utf-8'))
#使用decode解碼
with open('/tm/data.txt', 'r') as f:
data = f.read()
data.decode('utf-8')
如果需要寫入的字符串比較多,而每次都需要進(jìn)行編碼,程序?qū)兊梅浅5窒?。在Python2中可以使用codecs模塊,在Python3中內(nèi)置的open函數(shù)就已經(jīng)支持指定編碼格式。指定編碼格式以后,當(dāng)我們寫入時會自動將Unicode轉(zhuǎn)換為特定的編碼,讀取文件時,自動以特定的UTF進(jìn)行編碼。
在python2中,使用codecs模塊進(jìn)行編碼和解碼
import codecs
name=u'超人'
with open('/tmp/data.txt', 'w',encoding='utf-8') as f:
f.write(name)
with open('/tm/data.txt', 'r',encoding='utf-8') as f:
data = f.read()
在Python3中,內(nèi)置的open函數(shù)可以指定字符集編碼:
name='超人'
with open('/tmp/data.txt', 'w',encoding='utf-8') as f:
f.write(name)
...
4.4 Jinja2模版
4.4.1 模版介紹
模版在Python的web開發(fā)中廣泛使用,它能夠有效地將業(yè)務(wù)邏輯和頁面邏輯分離,使得工程師編寫出可讀性更好、更加容易維護(hù)的代碼。
試想一下,要為一個大型的表格構(gòu)建HTML代碼,表格中的數(shù)據(jù)由數(shù)據(jù)庫中讀取的數(shù)據(jù)以及必要的HTML字符串連接在一起。這個時候,最簡單也就是最直接的方式就是Python代碼中使用字符串拼接的方式生成HTML代碼。如果真的這么做了,對工程師來說將是個噩夢,而且代碼無法維護(hù)。
此時,可以使用模塊將業(yè)務(wù)邏輯與頁面邏輯分隔開來。模塊包含的是一個響應(yīng)文本的文件,其中包含用占位變量表示的動態(tài)部分,其具體值只在請求的上下文中才能知道。使用真實(shí)的值替代變量,再返回最終得到的響應(yīng)字符串,這一過程成為渲染。
web開發(fā)是最需要使用模版的地方,但是,并不是唯一可以使用模版的地方。模版使用范圍比大多數(shù)工程師接觸的都要廣泛,因?yàn)槟0媸褂盟谢谖谋镜母袷?,如HTML,XML,CSV等。使用模版能夠編寫出可讀性更好、更容易理解和維護(hù)的代碼,并且使用范圍非常廣泛,因此怎么使用模版主要取決于工程師的想象力和創(chuàng)造力。比如,我們在使用Ansible就會用到。作為工程師,我們也可以使用Jinja2管理工作中的配置文件。一旦學(xué)會使用模版管理配置文件,就可以拜托無數(shù)繁瑣的文本替換工作。
Python的標(biāo)準(zhǔn)庫自帶了一個簡單的模版,下面的代碼便是一個模版使用的例子。模版包含的是一個響應(yīng)信息,其中包含用占位變量表示的動態(tài)部分,動態(tài)部分的取值取決于具體的應(yīng)用,并且只有在請求的上下文才能知道。渲染就是使用真實(shí)的值替換變量,再返回最終得到的響應(yīng)字符串。
from string import Template
s=Template('$who is a $role')
s.substitute(who='bob',role='teacher')
s.substitute(who='lily',role='student')
Python自帶的模版功能非常有限,例如無法在模版中使用控制語句和表達(dá)式,不支持繼承和重用等操作。這對于web開發(fā)來說是遠(yuǎn)遠(yuǎn)不夠,因此,出現(xiàn)了第三方的模版系統(tǒng)。目前市面上有非常多的模版系統(tǒng),其中最知名的是Jinja2和Mako。
4.42 Jinja2語法入門
Jinja2模版引擎之所以使用廣泛,是因?yàn)樗哂幸韵聝?yōu)點(diǎn):
- 相對于Template,Jinja2更加靈活,它提供了控制結(jié)構(gòu)、表達(dá)式和繼承等,工程師可以在模版中做更多的事情。
- 相對于Mako,Jinja2提供了僅有的控制結(jié)構(gòu),不允許在模版中編寫太多的業(yè)務(wù)邏輯,避免了工程師的亂用行文。
- 相對于Django模版,Jinja2的性能更好
- Jinja2模版的可讀性很好。
Jinja2是Flask的一個依賴,如果已經(jīng)安裝了Flask,Jinja2也會隨之安裝。當(dāng)然,也可以單獨(dú)安裝Jinja2.
pip install jinja2
接下來將詳細(xì)介紹Jinja2的語法。
- 語法塊
Jinja2可以應(yīng)用于任何基于文本的格式,如HTML、XML等。Jinja2使用大括號的方式表示Jinja2的語法。在Jinja2中,存在3中語法:
- 控制結(jié)構(gòu) {%%}
- 變量取值 {{}}
- 注釋{##}
下面是使用Jinja控制結(jié)構(gòu)的和注釋的一個例子:
{# note: disabled template because we no longer use this
{% for user in users %}
...
{% endfor %}
#}
可以看到,for循環(huán)的使用和Python比較類似,但是,沒有了復(fù)合語句末尾的冒號。此外需要使用endfor作為結(jié)束標(biāo)志。Jinja2中的if語句也一樣,沒有復(fù)合語句末尾的冒號,需要使用endif作為結(jié)束標(biāo)志。
- 變量
Jinja2模版中使用的{{ }}語法表示一個變量,他是一種特殊的占位符,告訴模塊引擎這個位置的值在渲染模版時獲取。Jinja2識別所有的Python數(shù)據(jù)類型,甚至是一些復(fù)雜的類型,如列表、字典和對象等。如下:
A value from a dictionary: {{ mydict['key'] }}
A value from a list: {{ mydict[3] }}
A value from a list,with a variable index: {{ mydict[myintvar] }}
A value from a object's method: {{ myobj.somemethod() }}
- Jinja2中的過濾器
變量可以通過“過濾器”進(jìn)行修改,過濾器可以理解為Jinja2里面的內(nèi)置函數(shù)和字符串處理函數(shù)。例如,存在一個名為lower的過濾器,它的作用與字符串對象的lower方法一模一樣。下面列表是常見的Jinja2過濾器。
| 過濾器名 | 說明 |
|---|---|
| safe | 渲染值時不轉(zhuǎn)義 |
| capitalize | 把值的首字母轉(zhuǎn)換為大寫,其他字母轉(zhuǎn)換為小寫 |
| lower | 把值轉(zhuǎn)換為小寫 |
| upper | 把值轉(zhuǎn)換為大寫 |
| title | 把值中每個單詞首字母都轉(zhuǎn)換為大寫 |
| trim | 把值的首尾空格去掉 |
| striptags | 渲染之前把值中所有的HTML標(biāo)簽都去掉 |
| join | 拼接多個值為字符串 |
| replace | 替換字符串的值 |
| round | 默認(rèn)對數(shù)字進(jìn)行四舍五入,也可以用參數(shù)進(jìn)行控制 |
| int | 把值轉(zhuǎn)換為整型 |
在Jinja2中,變量可以通過"過濾器"修改,過濾器與變量用管道(|)分割。多個過濾器可以鏈?zhǔn)秸{(diào)用,前一個過濾器的輸出作為后一個過濾器的輸入,如下所示:
{{ "Helle World" | replace("Hello","Goodbye") }}
{{ "Helle World" | replace("Hello","Goodbye") | upper}}
{{ 42.55 | round }}
{{ 42.55 | round |int }}
- Jinja2的控制結(jié)構(gòu)
Jinja2中的if語句類似于Python中的if語句,但是,需要使用endif語句作為條件判斷的結(jié)束。我們可以使用if語句判斷一個變量是否定義是否為空,是否為真值。與Python中的if語句一樣,也可以使用elif和else構(gòu)建多個分支,如下所示:
{% if kenny.sick %}
kenny is sick.
{% elif kenny.dead %}
You killed Kenny!
{% else %}
Kenny looks okay
{% endif %}
- Jinja2的for循環(huán)
Jinja2中的for語句可以用于迭代Python的數(shù)據(jù)類型,包括列表、元組和字典。在Jinji中不存在while循環(huán),這樣符合了Jinja2的提供僅有的控制結(jié)構(gòu)的設(shè)計(jì)原則。
在Jinja2中迭代列表:
<h1>Members</h1>
<u1>
{% for user in users %}
<li>{{ user.username }}</li>
{% endfor %}
</u1>
在Jinja2中迭代字典:
<d1>
{% for key,value in d.iteritems() %}
<dt>{{ key }</dt>}
<dd>{{ value }}</dd>
{% endfor %}
</d1>
除了基本的for循環(huán)使用以外,Jinja2還提供了一些特殊的變量,我們不用定義就可以直接使用這些變量。
| 變量 | 描述 |
|---|---|
| loop.index | 當(dāng)前循環(huán)迭代的次數(shù)(從1開始) |
| loop.index0 | 當(dāng)前循環(huán)迭代的次數(shù)(從0開始) |
| loop.revindex | 到循環(huán)結(jié)束的次數(shù)(從1開始) |
| loop.revindex0 | 到循環(huán)結(jié)束的次數(shù)(從0開始) |
| loop.first | 如果是第一次迭代,為True,否則為False |
| loop.last | 如果是最后次迭代,為True,否則為False |
| loop.length | 序列中的項(xiàng)目數(shù) |
| loop.cycle | 在一串序列間取值的輔助函數(shù) |
有關(guān)宏和繼承函數(shù)的內(nèi)容,請看書籍。貌似一般的維護(hù)工作中也用不到的了。
- Jinja2的其他運(yùn)算
Jinja2可以定義變量,對變量進(jìn)行操作,Jinja2提供了算數(shù)操作、比較操作和邏輯操作。使用Jinja2模板時,應(yīng)該盡可能在Python代碼中進(jìn)行邏輯處理,在Jinja2中僅處理顯示問題。因此,一般很少用到Jinja2的變量和變量的運(yùn)算操作。常用的運(yùn)算操作:
- 算數(shù)操作 + - * / // % * **
- 比較操作 == != > >= < <=
- 邏輯運(yùn)算 not and or