2009年8月11日 星期二

正規表示式2

引用 http://fannys23.pixnet.net/blog/trackback/e150b26b3d/15367167


檢查工具 http://osteele.com/tools/rework/

1. 對正規表示式來說,任意字元是以點 (.) 表示。
e.g.
a...e 表示 a 與 e 之間有任意三個字元
ae←比對失敗
a12e←比對失敗,因為不足三個字元
abcde←比對成功

2. 中括號中表示指定特定的字元,若其中一個符合則符合
e.g.
a[abc]e
比對成功的例子: aae, abe, ace
比對失敗的例子: aze

3. 小寫的檢查: a-z,大寫的檢查: A-Z,數字的檢查: 0-9
e.g.
a[0-9a-zA-Z]e
在 a 與 e 間插入多個英數字或插入一個以上的特殊符號外,其他都符合規則

4. 在中括號之中的點 (.) ,僅代表一個點。
e.g.
a[.]e
只有 a.e 符合

5. 符號 ^ 在第一個字元出現時有 not 的意思
e.g.
a[^0-9a-zA-Z]e 表示英數字以外的符號符合此項比對

6. 符號 ^ 在第一個字元以外的地方出現,代表 ^ 本身這個字
e.g.
a[0-9^a-zA-Z]e 表示 a 和 e 間出現一個 ^ 或一個英數字均符合

7. 修飾詞:
星號 (*) 可用來代表零或多個
e.g.
a.*z 若字詞頭為 a 尾為 z 則符合
ab*z 若字詞頭為 a 尾為 z ,且中間出現一個以上的 b 則符合
ab.*z 字詞頭為 ab 尾為 z 則符合

問號 (?) 代表零個或一個
w.?e
符合的範例:we、wie
不符合的範例:willie

加號 (+) 代表一個或多個
e.g.
a.+z 在 a 和 z 之間出現一個或以上的字元即符合

若希望在 a 與 z 之間有一個以上非英文大寫的任意字元,
寫法為: a[^A-Z]+z

8. 大括號用來精確比對前一個字
ab{5}z ←僅 abbbbbz 符合
ab{1,5}z ←表示 b 出現最少一次、最多五次
a[A-Z]{1,5}z ←中間可以有一到五個大寫英文字

9. 意義相同的正規表示式:
b{1,} = b+
b{0,} = b*

10. 逸出字元前要加上反斜線
a\.b
a\[b
a\\b

11. 群組(grouping): 用小括號包起來
a(abc)*z ←表示 a 開頭、z 結尾,中間出現任意次數的 "abc"
另外也有記憶小括號的功能
e.g.
import re
m = re.search('it is (fine (today))', 'it is fine today')
m.group(0)
m.group(1)
m.group(2)
#以上程式會依續印出完整字串、左起第一組小括號、第二組小括號

12. 較短的表示方式:
http://www.amk.ca/python/howto/regex/
\w = [a-zA-Z0-9_]
\s = [\t\n \r\f\v]
\d = [0-9]      ←所以 IP 比對可以改寫成 \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}
            還可以再縮寫成 \d{1,3}(\.\d{1,3}){3}

大寫則有反義的用途,
例如 \D 是非數字,\W 代表非英數字、\s 代表非空白字元

13. 字和空白之間的交會點是 \b
因此 "Will and Willie are good friends."
可以利用 Will\b 找出 Will (以免同時也比對到 Willie)

14. 正規表示式預設有「貪多」的特性
import re
reg = re.search("t.*d", "today is fine")
reg.group()

這樣的搜尋會一路找到結尾、再找回來、才取出 tod,
會造成效能上的耗費,因此有不貪多演算法,
\.*? ←代表抓取任意字元、任意次數、不貪多
\.+? ←代表抓取任意字元、一次以上、不貪多

不貪多演算法的說明
http://www.gais.com.tw/article.php?u=DeeR&i=20080225

15. 把沒有明顯分隔符號的字串切割重組
e.g.
import re
text = "willie123good456"
"".join(re.split(r"\d+", text))

16. 使用其他人寫好的套件剖析 XML 與 HTML

HTML:
Beautiful Soup
http://www.crummy.com/software/BeautifulSoup/

Parsing HTML 的說明
http://www.crummy.com/software/BeautifulSoup/documentation.html#Parsing%20HTML

XML:
ElementTree
http://effbot.org/zone/element-index.htm
Parsing XML 的說明
http://docs.python.org/lib/module-xml.etree.ElementTree.html



【實作練習: 剖析 log 中異常的 IP】
#假設 IP 為 200 開頭的是異常 IP
#!/usr/bin/python

import re
f = open('/tmp/auth.log')
for i in f:
regex = re.search(r'200\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}', i)
if regex:
print regex.group()
f.close()

#re.search 表示比對正規表示式與輸入結果
#第一個參數是正規表示式,第二個是輸入內容
#regex.group() 預設參數是 0

tips:
小括號包起來的東西在 python 裡會被記憶,
因此若只想取 ip 最後一段,可改寫如下:

import re
f = open('/tmp/auth.log')
for i in f:
regex = re.search(r'200\.[0-9]{1,3}\.[0-9]{1,3}\.([0-9]{1,3})', i)
#加上小括號
if regex:
print regex.group(1)
#加上小括號所出現的 index (從 1 開始算)
f.close()



【實作練習:分析 log 檔中的非法使用者想入侵的帳號】

#!/usr/bin/python

import re
f = open('/tmp/auth.log')
for i in f:
regex = re.search(r'Invalid user ([^ ]+) from ([^ ]+)', i)
if regex:
print regex.group(1) + " => " + regex.group(2),
f.close()



【實作練習:分析 log 檔中的非法使用者想入侵的帳號 (改善執行效能)】

#!/usr/bin/python

import re
f = open('/tmp/auth.log')
rec = re.compile(r'Invalid user ([^ ]+) from ([^ ]+)')
for i in f:
regex = rec.search(i)
if regex:
print regex.group(1) + " => " + regex.group(2),
f.close()



【實作練習:分析 log 檔中的非法使用者想入侵的帳號 (縮短正規表示式)】

#!/usr/bin/python

import re
f = open('/tmp/auth.log')
rec = re.compile(r'Invalid user (\w+) from ([^ ]+)')
for i in f:
regex = rec.search(i)
if regex:
print regex.group(1) + " => " + regex.group(2),
f.close()



【實作練習:取出 HTML 的部分內容】

from BeautifulSoup import BeautifulSoup
f = open('test.htm')
html = f.read()
f.close()
soup = BeautifulSoup(html)
soup.html.body.span.string         #取出span標籤內夾記的內容
soup.html.body.a.string          #預設會取出第一個找到的 a 標籤夾記的內容
soup.html.body('div')[1].a.string     #取得第二組 div 內的 a 標籤
soup.html.body.div.a['href'] #抓出 a 標籤中的屬性 href



【實作練習:取出 XML 的部分內容】(for python 2.5)

from xml.etree.ElementTree import XML
myxml = open('test.xml').read()
seek = XML(myxml)
seek.getchildren() #確認 seek 可找到哪些子節點
seek.find('staff').find('name').text #取出子節點 staff 中的 name 裡頭的內容
for i in seek.findall('staff'): #找出所有的 staff
print i.find('name').text #取出 staff 中的 name 內容

沒有留言: