第4章 文本處理

文本是軟件工程師日常工作中處理最多的數(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()等。

  1. 通用操作
    如 len('pcm')、 'c' in 'pcm'

  2. 與大小寫相關(guān)的方法

  • upper: 轉(zhuǎn)為大寫
  • lower: 轉(zhuǎn)為小寫
  • isupper: 判斷是否為大寫
  • islower: 判斷是否為小寫
  • swapcase: 大寫轉(zhuǎn)小寫,小寫轉(zhuǎn)大寫
  • capitalize: 將首字母轉(zhuǎn)換為大寫
  • istitle: 判斷字符串是不是標(biāo)題
  1. 判斷類方法
    除了前面介紹的幾個is開頭的方法,還有下面的這些也是很常見的:
  • isalpha: 是否只包含字母
  • isalnum: 是否只包含字符串字母和數(shù)字
  • isspace: 是否只包含空格、制表符、換行符
  • isdecimal: 是否自包含數(shù)字字符
  1. 字符串方法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)
  1. 查找類函數(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')
  1. 字符串操作方法
    字符串的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á)式來匹配文本。例如:

  1. 要匹配給定文本中的所有單詞
#"?"用于匹配單詞前后可能出現(xiàn)的空格,[a-zA-Z]+代表一個或多個英文字母
?[a-zA-Z]+
  1. 要匹配一個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方法

  1. 匹配類的函數(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ù)。

  1. 修改類函數(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("'"))
  1. 大小寫不敏感
    在re模塊中,要忽略大小寫的差異,就如同前面的示例那樣,使用flags=re.IGNORECASE。

  2. 非貪婪模式
    在正則表達(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的語法。

  1. 語法塊
    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)志。

  1. 變量
    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() }}
  1. 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 }}
  1. 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 %}
  1. 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ù)工作中也用不到的了。

  1. 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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容