顯示具有 程式設計 標籤的文章。 顯示所有文章
顯示具有 程式設計 標籤的文章。 顯示所有文章

2011-03-09

Regular expression 常用驗證

Regular expression 常用驗證

資料來源: http://readily-notes.blogspot.com/2010/09/regex.html


身分證




身分證
([A-Z]|[a-z])\d{9}
網址
http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?
電子郵件
\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*
郵遞區號
\d{3}((-)?\d{2})? 
電話
[0-9]{2}\-[0-9]{7}
手機
[0-9]{4}\-[0-9]{3}\-[0-9]{3}


正整數
^\d*$ 
整數
^([+-]?)\d*$
小數點
^([+-]?)[1-9]\d*\.\d*$


驗證 IP
^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$
驗證時間 (HH:MM:SS)
([0-1][0-9]|2[0-3])\:[0-5][0-9]\:[0-5][0-9]
驗證日期 (YYYY/MM/DD)
^[0-9]{4}/(((0[13578]|(10|12))/(0[1-9]|[1-2][0-9]|3[0-1]))|
(02/(0[1-9]|[1-2][0-9]))|((0[469]|11)/(0[1-9]|[1-2][0-9]|30)))$


HTML 標籤
/^<([a-z]+)([^<]+)*(?:>(.*)<\/\1>|\s+\/>)$/
使用者名
^[a-z0-9_-]{3,16}$
密碼
^[a-z0-9_-]{6,18}$


中日韓符號區。收容康熙字典部首、中日韓輔助部首、注音符號、日本假名、韓文音符,中日韓的符號、標點、帶圈或帶括符文數字、月份,以及日本的假名組合、單位、年號、月份、日期、時間等。
\u2e80~\u33ffh
中日韓認同表意文字擴充a區,總計收容6,582個中日韓漢字。
\u3400~\u4dffh
中日韓認同表意文字區,總計收容20,902個中日韓漢字。
\u4e00~\u9fffh
彝族文字區,收容中國南方彝族文字和字根。
\ua000~\ua4ffh
韓文拼音組合字區,收容以韓文音符拼成的文字。
\uac00~\ud7ffh
中日韓兼容表意文字區,總計收容302個中日韓漢字。
\uf900~\ufaffh
文字表現形式區,收容組合拉丁文字、希伯來文、阿拉伯文、中日韓直式標點、小符號、半角符號、全角符號等。
\ufb00~\ufffdh


參考網站:
http://hi.baidu.com/liaidongyinhu/blog/item/2b72ed7978b4eef90bd187d0.html
http://www.dotblogs.com.tw/mouse777/archive/2009/01/13/6780.aspx
http://www.wretch.cc/blog/bigdstut/12932356
http://zh.wikipedia.org/zh-hk/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F

2008-10-16

[轉載]程式設計師的格言 , but, or bug

在網路上看到一篇有趣的文章,轉載上來與大家分享


以下內容轉載自:

http://buttaiwan.wordpress.com/2008/10/12/programmers_rule/


but, or bug

程式設計師的格言



譯自
http://www2.biglobe.ne.jp/~oni_page/other/etc/pr03.html
http://mixi.jp/view_community.pl?id=1772737

(版本2 2008/10/12更新)

譯註
SE是日本軟體公司裡程式設計師的頭子。自己不太寫程式,主要工作是跟客戶確認規格。
程式設計師多半自己不面對客戶。
跟PM又不一樣。(有什麼比較貼切的職稱翻譯嗎?)

—————

1
每天有24小時。
所謂的「今天之內」,是指到明天早上為止。

2
程式不會照自己所想的跑。只會照所寫的跑。

3
需求規格在程式寫完後才會敲定。
基本規格要客戶看到成品後才會決定。
詳細規格要使用者用過後才會確定。

4
我對軟體設計的方式導出的結論,有兩種方式。
一是把軟體設計得單純到很明顯不會有缺陷,
不然就是把軟體設計得複雜到沒有明顯的缺陷。
- C.A.R.Hoare

5
程式碼不要在開發現場寫! 去客戶那寫!
除錯不要在期限前做! 上線後再做!

6
畫面藍了。

7
先說「沒辦法」的人贏。

8
有意見的話你寫

9
要殺一個程式設計師不需要刀,改三次規格就好

10
首先要先懷疑別人,被懷疑的人或許會把問題解決掉。
(註:通常會「先懷疑自己」)

11
開發沒有終點。只有釋出(release)。

12
無論規格多晚才能確定,結案期限永遠不會變。
這是所謂的「期限守恆定理」。

13
客戶總是覺得水跟追加需求是不用錢的。

14
付錢愈計較的客人愈囉唆。

15
在排定開發行程時,總是視而不見一些連小學生都會的算數。
業務部門總是一堆不知道1+1=2的人。

16
一個人掛了大家都掛了。

17
bug過了一晚可能就變成規格了。

18
好的規格找一個天才不如找三個凡人。
爛的規格找一百個凡人不如找一個天才。

19
客製軟體中30%的價格用在確認規格上。
30%用在修改規格上。
30%用在找bug。
結果初期規格反映在價格上占的比例只有10%。

20
對客戶來說SE是部下,程式設計師是家畜。
對SE來說客人是錢,對程式設計師來說顧客是看不見的病毒。
除了弄完程式以外,沒有其他驅除的辦法。

21
顧客想受SE喜歡,要自己了解到系統開發需要時間與金錢,早點確定規格。
SE想受顧客喜歡,則要讓程式設計師討厭自己。

22
很多SE跟程式設計師都暗自想著有錢有閒的話什麼系統都想自己動手做,
不過都沒這種機會。

23
品質的劣化程度依規格改變的次數與規模而定。

24
業務是認為空想能夠實現的夢想家。
SE則是深信任何障礙都能突破的冒險家。
程式設計師則是被夢想家和冒險家拋到漆黑海裡的漂流者。

25
有才能的程式設計師第一次看到設計細節時,要先理解程式的目的。
接下來要設法讓SE了解到以指定的方法、工時並無法完成這個工作。

26
程式是運氣與直覺堆砌而成的奇蹟。
若不具備這兩者,不可能以這樣的工時實現這樣的規格。
修改規格是對奇蹟吐槽的褻瀆行為。
而追加修改則是相信奇蹟還會重現的無謀行動。

27
程式設計師聽了「把自己當作顧客去著想!」而開始思考。
啊,像夢一樣。

28
對於因為興趣而寫程式的人來說,所謂的技術是程式語言能力。
對於因為工作而寫程式的人來說,所謂的技術是邏輯思考能力與人際溝通能力。
程式語言可以看著手冊溝通,客戶不行。

29
程式系統在交貨之前會不斷縮小。
先用元件定義取悅老闆。
再拿經費概算要部長妥協現實的方案。
在運用會議中,課長會嘗識減少自己責任範圍。
在細節會議中,負責人會把範圍縮到自己記得的部分。

30
SE需要持久力,程式設計師需要爆發力。

31
準時離開公司,工作會變多。

32
完美的程式需要完美的時間與金錢。
聽說揮霍著美國的國家預算的NASA,也覺得時間跟錢不夠。

33
詳細設計要在程式碼的註解裡做完。
註解是唯一的自衛手段,至少要讓自己看懂。

34
還有時間看程式碼的話就執行他。
CPU跑得比腦細胞快。至少這時候可以休息。

35
程式的異常該稱為「bug」還是「規格上的限制」是看期限還剩多久決定的。

36
所謂便服日,好像社會上把他叫做假日
(註) 日本有些公司會有所謂便服日(不用穿西裝的日子),通常是星期五,但…

37
地獄持續一段時間後,充滿殺氣的怒吼會變多。
再持續一段時間,說話會變少但牢騷會變多,壟罩在凝重的氣氛裡。
再持續下去,反而會海闊天空,四周洋溢充滿活力的聲音。
這種狀態稱為「Programmer’s High」,也是倒下來的人開始出現的時候。

38
遠處的火災一定燒到這裡。

39
禱告,然後跑吧。

40
程式不是用腦記的,要用身體記住。

41
明天能放假的話死了也罷。

42
外面有下雨耶,昨天開始下的嗎?

43
若不能心靜不移,身體會掛。
若不讓自己殘忍,自己會被殺。

44
客戶會說謊,業務會作夢,SE會做白日夢。
程式設計師則惦惦。(愈來愈自言自語)

45
(日文文字遊戲)
SE總是不負責的說「別逞強」,
業務總是無理取鬧不准說「沒辦法」。

46
規格書就像航海圖,客戶則是洋流。洋流陰晴不定,航海圖就變垃圾。
程式設計師必須在沒有航海圖的海上憑自己的力量找到大陸。

47
再嘮嘮叨叨下去也是要付錢的。

48
多想個10秒鐘,你可以不說「嗯,這個做得到」。

49
人是無法從別人失敗記取教訓的動物。
砍成本、改規格、加需求、趕上線,從來沒有人從眾多失敗中記取教訓。

50
老手用來提振精神的魔法格言:
「不過比起以前來說算是…」
新人用來提起幹勁的魔法格言:
「把這件工作做完的話…」他們還不知道工作是沒有終點的。

51
所謂交案期限,是指開發現場從公司換到客戶那裡的日子。

52
程式、SE、經理不是職務。是逃不掉的責任。

53
業務是最難搞的客戶。

54
能夠迅速想到解法的程式設計師太多了。
他們能用一分鐘想到方法,用一天去寫程式。
不需要花一小時想到解法,再用一小時去寫程式。
- Jon Bentley

55
漂亮的規格,可以從沒有bug出現看出來。
明明爛的就是設計,為什麼是這樣…

56
上線後的除錯才叫做bug。

57
追加需求確定後交貨期限就無法確定,
交貨期限確定後追加需求就無法確定。
這稱為「追加需求與交貨期限的測不準原理」。

58
除三個錯就會冒出一個錯。
這稱為bug的無窮迴圈。

59
不祥的預感總會實現。
不過程式設計師不會去煩惱不祥的預感,那是SE的工作。

60
要解決地獄的辦法,就是客戶把錢交出來。

61
不懂電腦的操作者是發現bug的天才。而且無法重現。

62
每次開會就更改規格的客戶,
他的操作手冊要等到操作寫好的程式後才能寫出來。

63
搞不懂的時候,Currency(長整數)比Interger(整數)好用。
Variant(字串、數字都能存的萬能變數)又比Currency(長整數)好用。
安全第一。
(VB程式設計師如是說)

64
啊,那是微軟的規格。

65
程式設計師所不滿的規格也一定會讓客戶不滿。
(這是說程式設計師覺得難寫的地方常常是SE溝通有落差)

66
程式設計師需要的技能,
包括交涉、時程管理、業務分析、提案、設計、程式語言、架構、維護、使用。
SE需要的技能則減掉程式語言、架構、維護與使用。
專案經理需要的能力則再減掉業務分析、提案與設計。
業務需要的能力再扣掉時程管理。

67
正因為健康,才能做不健康的事。

68
規、規格、是規格啦。不過有一點跟規格不太一樣啦。

69
那是你說的規格。

70
開發室沒有窗戶,那是因為以前…

71
爛了也是因為規格。

72
SE: 真沒辦法。
PG: 也沒註解。
(碰到不知道是誰寫的程式,大家都束手無策的狀態)

73
為什麼你不能兩三下解決掉他啦。
因為之前兩三下搞定的東西也被你兩三下就否定了。

74
不會動的bug就只是普通的bug。(會動的bug則能視為規格)

75
今天好好清理bug,bug應該死光了吧。
咦?Windows也死了唷。

76
客戶不會去想最壞的情況。要他面對最壞的情況,他會認為是漫天開價。
SE則會顧慮最壞的情況,準備應付最壞的情況。
程式設計師比誰都早預料到最壞的情況,而無視最壞的情況。

77
唯一不產生bug的方法,就是不寫程式。
第二好的方法,就是在時程跟人員確定之後的每次改規格,都重新檢視過整個專案。

78
共同責任是程式設計師的責任。
管理職?那是啥?好吃嗎?我沒吃過耶。

79
如果可以改行的話,想找個準時下班不叫「逃跑」的工作。

80
對職業程式設計師來說,漂亮的程式是單純而自然的邏輯、簡單而基本的指令、豐富的註解,
也就是新手程式設計師也能馬上動手改的程式。
而要寫租這樣的程式,需要單純、簡單、美麗的規格。
但可惜客人總是喜歡搞很複雜。

81
設計者應該是不該要求製作者製作出超過設計以上內容的吧…

82
無論是做的比規格書裡的多,還是只照規格書裡的寫,SE都會找程式設計師的碴。
所以程式設計師只做規格書裡的寫的內容。

83
SE對程式設計師說的「常識」每三小時變一次。

84
自己看規格書。不能跑的是規格。

85
「沒辦法」是要看把一天當多少小時來算。
一天常常指的是3人日,一個月常常是指4.5人月喔。

86
工時要減掉一半的單體測試與一半的系統測試,
而交貨期則要另外加上上線後的兩個月。

87
能拿到錢的規格變更稱為「受理項目」,
拿不到錢的規格變更則稱為「SE的規格確認失誤」。
程式設計師是這麼看的。

88
累了。我想睡了。可以回家嗎。
(累了吧,我也累了。好累喔怎麼了。反正就是規格啦,管他的)

89
試圖降低成本的話,為了配合預算,品質會下降,不過漫天開價做出來的品質也不見得好到哪裡去。

90
REDO到底該怎麼唸一直搞不懂。是利斗嗎、李度嗎、R E D O嗎,難道是 red 零 嗎? 拜託加上注音吧。
(譯註:我比較煩惱 Linux)

91
有人在程式碼註解裡寫日記。像「今天是雨天…」,「想回家…」之類的。甚至還有「修改日: 2003/10/10 不能同意你更多」這種註解出現。說到這個,好像也看過「吃大便」這樣的註解。

92
小學生時第一次看到電腦
國中時第一次學會怎麼用
高中與大學學會程式語言
出社會後才發現自己走錯路

93
「不要讓老闆當業務比較好」

94
說來說去,要去研究根本不知道為什麼會動的東西為什麼不會動了,找拿破崙來也沒搞頭。

————————

ex 1
就算程式裡沒bug,編譯器會有bug。
就算編譯器沒bug,OS會有bug。
就算一切都沒bug,客戶會決定什麼是bug。

ex 2
規格與規格書是不同的東西。

ex 3
比期限更重要的是靈感與睡眠。

ex 4
比知識與經驗重要的是手冊與時間。

ex 5
能動就好了,能動的話…

ex 6
過了三天就是別人寫的程式碼。

ex 7 (大搜查線系列)
規格變動不是在會議室裡發生的!是在現場發生的!

ex 8 (大搜查線系列)
異常不是在模擬測試時發生的!是上線後才會發生的!

ex 9
漂亮的設計三天或許就膩了
骯髒的設計三天就習慣了

ex 10
bug與規格是一體兩面

ex 11
電腦裡沒有bug,bug常在人心。

ex 12
無論怎麼檢查,不管怎麼確認,上線前一晚就是睡不著。(RFC968)

ex 13
估價需要1%的經驗與99%的直覺

ex 14
沒有什麼事情比直接讓找不到任何bug的程式直接上線還要可怕的了。

ex 15
・『程式設計師』=能將SE條理不通的說明翻譯成程式碼的高手
・『SE』=與客戶討論改寫規格書、與程式設計師討論後再改寫規格書,程式出貨後還要繼續改寫規格書的人
・『PM』=每天修改自己定下的行程表的人
・『業界老鳥』=臉色蒼白缺乏表情的人
・『外包』=幫不會寫程式的正職員工寫程式的人
・『coding』=複製貼上的工作
・『單體測試』=指開始寫程式
・『除錯』=把程式碼註解掉的工作
・『新同事』=在火燒屁股的專案火上加油的人
・『出貨日』=把只完成一半的系統上線的日子
・『末班電車』=業界平均的下班時間
・『颱風假』=一年一度可以準時下班的業界假日

ex 16
當誰寫的程式碼跑出bug時,那個人大概都不在了(墨菲定理?)

ex 17
最終手段
「重開機」
意外的常常都很有效

ex 18
最強藉口
以前「那是硬體的極限」
現在「那是Windows的規格」

ex 19
「程式碼的可信度,不會比寫的人還可信。」


2007-11-05

Regular Expression的表示法說明

原文來源

Regular Expression的表示法說明

  • Regular Expression 的表示字串的內容主要可以分成兩大類:
    1. Characters:一般字元(文數字元),所代表的意義與原字面的意義相同。
    2. Operators:特殊字元(非文數字字元),代表某種特殊規則的意義。若要取消其代表意義,必須使用「\」跳脫字元。
  • 例如:
    1. "foo" 的 RE 表示字串 "foo" 的意思。
    2. "[Ff]oo" 的 RE 表示字串 "Foo" 或字串 "foo" 的意思。
    3. "F*"的 RE 表示F這個字元會出現0次或多次。
    4. "F\*"的 RE 表示字串 "F*"的意思。

RE 的特殊字元-1

  • 「.」句號(period) 代表任意一個字元,如 /.at/ 可符合 bat, cat, rat等任何字開頭,結尾是at的單字。
  • 「^」(carat),表示位置在開頭,如 /^Mary/ ,代表以 Mary 開頭。
  • 「$」(dollor-sign),表示位置在結尾,如 /Mary$/ ,代表以 Mary 結尾。
  • 「[ ]」中括號(brackets)代表集合中的任一字元,如/[01256]/ 代表0,1,2,5,6這個集合中的任何一個字元。
  • 「-」連字號(dash)在中括號內表示「範圍」,如/[0-9]/代表0到9的任一個單一的數字。
  • 「^」(carat)在中括號內表示「否定」,/[^aeiou]/ 代表除了a,e,i,o,u這幾個母音之外的字元。

RE的特殊字元-2

  • 「|」(pipe)表示可選擇的,如/cat|dog|bird/代表cat, dog, bird 其中之一皆可(or的概念)。
  • 「?」問號(question mark) 表示前面的字元或集合出現0次或1次,如/colou?r / 代表u這個字可出現也可不出現。
  • 「+」加號(plus) 表示前面的字元或集合出現1次或多次,如/ap+le / 代表p這個字至少出現1次。
  • 「*」星號(asterisk)表示前面的字元或集合出現0次或多次,如/section [0-9]*/ 代表數字可出現也可不出現。
  • 「{}」大括號(curly-braces)表示前面的字元或集合出現的次數,如/c{5,8} / 代表c這個字重複出現5到8次。
----另外有人整理出下面這些.可以參考看看--- from: http://www.brad.tw/regularexpressions
  • 描述出組成字串的元素
    • 直接列出字元
    • [] : 字元集合任取一個元素.
      • [ - ] : 字元的範圍區間
      • [^ ] : 字元集合之反集合任取一個元素.
    • . : 表示任一字元
    • * : 表示前一個字元或是前一個RE出現 0 或多次
    • ? : 表示前一個字元或是前一個RE出現 0 或 1 次 (延伸)
    • + : 表示前一個字元或是前一個RE出現 1 或 多次 (延伸)
    • \{n\} : 表示前一個字元或是前一個RE出現 n 次 (延伸)
    • \{n,m\} : 表示前一個字元或是前一個RE出現 n 到 m 次 (延伸)
    • \{n,\} : 表示前一個字元或是前一個RE至少出現 n 次 (延伸)
    • \<word\> : 表示一個英文字, 凡是以 tab, 逗點, 據點等標點符號, 空白字原所分開的英文字為單位.
    • \<word: 表示一個英文字, 字首為 word.
    • word\>: 表示一個英文字, 字尾為 word.
    • (word): 表示一個元素, 通常搭配後面表示法使用 (延伸)
    • | : 或, 兩個元素其中一個皆可 (延伸)
      • egrep '^(Linux|Windows)' test2
      • egrep -v '^#|^$' /etc/squid/squid.conf
  • 描述字串出現的位置
    • ^ : 每列的列首位置
    • $ : 每列的列尾位置
  • Escape Character 跳脫字元(\), 表示其後的特殊符號視作普通文字
  • ^^ : 表示 ^ 字元
  • $$ : 表示 $ 字元

[轉載]Regular Expression 教學

文章原始出處
作者 : hkln(HKLN.net)


[分享] Regular Expression 新手入門

Regular Expression (正規表示式,簡寫 regexp或regex)
是一個字串,可用來描述字串的模式,
例如我要表達「兩個英文大寫字母」,那麼 regexp 就是 [A-Z]{2}。
而使用這個 regexp ,就可以在一個字串內找出配合 (Match) 的部份,
例如在字串 $st = '11AA22BB33' 內,有哪些部份配合「兩個英文大寫字母」這個模式?
就會找到 'AA' 和 'BB' 兩部份了。

以下是一些常見的 regexp 應用:

use strict;
my ($st, $pattern, $replacement, @array, @matches);

##
## 有沒有找到至少一個 Match?
##
$st = '11AA22';
$pattern = 'AA';

if ($st =~ /$pattern/) {
print "has at least one match\n";
} else {
print "no match\n";
}


##
## 找出所有 Matches
##
$st = '11AA22BB33';
$pattern = '[A-Z]{2}';

@matches = $st =~ /$pattern/g;

foreach my $i (0 .. $#matches) {
print "$i : $matches[$i]\n";
}


##
## 找出所有 Matches,並取代它們
##
$st = '11AA22AA33';
$pattern = 'AA';
$replacement = 'XX';

$st =~ s/$pattern/$replacement/g;

print "$st\n";


##
## 在 Matches 的位置分割
##
$st = '11AA22AA33';
$pattern = 'AA';

@array = split /$pattern/, $st;

print join ',', @array;



以下是 .Net 例子(以 C# 語言)

using System;
using System.Text.RegularExpressions;

class MainClass
{
public static void Main(string[] args)
{
string st, pattern, replacement;
string[] array;
MatchCollection mc;

//
// has at least one match?
//
st = "11AA22";
pattern = @"AA";

if ( Regex.IsMatch(st, pattern) ) {
Console.WriteLine("has at least one match");
} else {
Console.WriteLine("no match");
}

//
// get all matches
//
st = "11AA22BB33";
pattern = @"[A-Z]{2}";

mc = Regex.Matches(st, pattern);

for (int i = 0; i < mc.Count; i++ )
{
Console.WriteLine("{0} : {1}", i, mc[i]);
}
//
// search and replace
//
st = "11AA22AA33"; pattern = @"AA"; replacement = "XX";
st = Regex.Replace(st, pattern, replacement);
Console.WriteLine("{0}", st);
//
// split
//
st = "11AA22AA33"; pattern = "AA";
array = Regex.Split(st, pattern);
Console.WriteLine(String.Join(",", array));
}
}


[[解釋 regexp 的意思]]
當您遇到一些很難看得懂的 regexp, 您可以用 YAPE::Regex::Explain 模組, 把 regexp 翻譯成英文,這對新手來說有很大幫助。

#### use YAPE::Regex::Explain;
my $pattern = '[A-Z]{2}';
print YAPE::Regex::Explain->new($pattern)->explain;
####

輸出結果:
[A-Z]{2} any character of: 'A' to 'Z' (2 times)
意思是「任何一個由 'A' 至 'Z' 的字元(兩次)」,
那麼它配合的東西就是:
AA
AB
...
AZ
BA
BB
...
ZA
ZB
...
ZZ
------------------------------------------------------------------------------------------------

>我目前所碰到的瓶頸是
>有沒有可能使用Regular Expression來判斷輸入的是數字,並且限定其數值範圍0.0

參考 perlretut 內的 "Building a regexp" 一節

------------------------------------------------------------------------------------------------

[[ 特殊字元 (Metacharacter) ]]

當您要表示一些特殊字元,例如括號,那就不能直接寫:
$pattern = '(';

而要在前面加上反斜號:
$pattern = '\(';

以下 regexp 的特殊字元:

{}[]()^$.|*+?/

為什麼這些是特殊字元呢?本文稍後會介紹。

例:
####
$st = '11((22';
$pattern = '\(\(';

@matches = $st =~ /$pattern/g;

foreach my $i (0 .. $#matches) {
print "$i : $matches[$i]\n";
}
####

順帶一提,應儘量用單引號來括字串,
因為它不會好像雙引號那樣會做字串插入,如果寫:
$pattern = "\(\(";

那結果變成:
$pattern = '((';

------------------------------------------------------------------------------------------------

[[ 脫離序列 (Escape Sequence) ]]

當您要表示 tab、 newline 等不能列印的字元,
可以用 Escape Sequence。

例:
$st = "11\t22\t33";
$pattern = '\t';

------------------------------------------------------------------------------------------------

[[ 字元類別 (Character Class) ]]

您除了可以表示一個固定的字元外,如
$pattern = 'A';

還可以表示一個字元集合,例如您想表示
「'A'字元或者'B'字元或者'C'字元」,可以寫
$pattern = '[ABC]';

即把您想 match 的字元寫在中括號內。

例:
$st = '11A22B33C44D';
$pattern = '[ABC]';

會找到以下的 match:
0 : A
1 : B
2 : C

例如您想找 'AAAXX' 或者 'AABXX' 或者 'AACXX':

$st = '11AACXX22AABXX33AAAXX44AAD';
$pattern = 'AA[ABC]XX';

會找到以下的 match:

0 : AACXX
1 : AABXX
2 : AAAXX

留意一點,雖然 $pattern 內的 [ABC] 是先寫A,
但結果是先找到 'AACXX',而不是'AAAXX',
因為 Perl 是先從 $st 的左邊開始找,而不是先從右邊開始找,
而 'AACXX' 是最左邊的 match。

------------------------------------------------------------------------------------------------

[[ 字元類別 - 脫離序列 ]]

在字元類別內表示一些特殊字元,也是要加反斜號,
不過字元類別內的特殊字元是:
-]\^$


本文稍後會解釋為何它們是特殊字元。

例:
$st = '11A22B33-44]';
$pattern = '[AB\-\]]';

會找到以下的 match:
0 : A
1 : B
2 : -
3 : ]

------------------------------------------------------------------------------------------------

[[ 字元類別 - 範圍運算子 (Range Operator) ]]

您可以用減號,來表示一段字元範圍,例如
$pattern = '[ABCDEFGHIJ]';

可以寫成
$pattern = '[A-J]';

這樣就更簡潔了。又例如
$pattern = '[ABCDKLMN0123]';

可以寫成
$pattern = '[A-DK-N0-3]';

但如果把減號寫在最頭或者最尾,例如
$pattern = '[-AB]';
或
$pattern = '[AB-]';

那麼減號就直接表示 '-' 字元,而不是範圍運算子了。

例:
$st = '99A88L773]';
$pattern = '[A-DK-N0-3]';

會找到以下的 match:
0 : A
1 : L
2 : 3


------------------------------------------------------------------------------------------------

[[ 字元類別 - 反相字元類別 (Negated Character Class) ]]

例如要表示「'A'或者'B'或者'C'」,就寫
$pattern = '[ABC]';

但如果要表示「'A'或者'B'或者'C'」的相反,即「不是'A'並且不是'B'並且不是'C'」
就可以寫
$pattern = '[^ABC]';

在字元類別內,如果第一個字元是 '^',就表示這是一個「反相字元類別」。

例:
$st = '11A22B33X44Y';
$pattern = '[^A-C0-9]';

會找到以下的 match:
0 : X
1 : Y

------------------------------------------------------------------------------------------------

[[ 字元類別 - 簡寫 ]]

Perl 提供了一些常用的字元類別簡寫,例如
$pattern = '[0-9]';

其實可以寫成
$pattern = '\d';

這就更簡潔了。以下列出這些簡寫:

\d 等於寫 [0-9]
\D 等於寫 [^0-9] ,即 \d 的相反
\w 等於寫 [0-9a-zA-Z_] ,即數字、英文大小寫字母和底線
\W 等於寫 [^0-9a-zA-Z_] ,即 \w 的相反
\s 等於寫 [\ \t\n\r\f] ,即白格字元 (Whitespace character),
包括space, tab, newline, carriage return, form feed

\S 等於寫 [^\ \t\n\r\f] ,即 \s 的相反

還有一個很特別的簡寫,就是點號 '.',
因為它比較複雜,所以稍後再介紹它。

例:
$st = '11A22b33_44=';
$pattern = '\D';

會找到以下的 match:
0 : A
1 : b
2 : _
3 : =


例:
$st = '11AA22A_33A044AX';
$pattern = 'A[^\dX-Z]';

會找到以下的 match:
0 : AA
1 : A_

------------------------------------------------------------------------------------------------

[[ Alternation ]]

如果想表示「'AB'字串或'KLM'字串或者'PQRST'」,可以寫
$pattern = 'AB|KLM|PQRST';
即用直線分隔它們。

Alternation 跟 Character Class 很像,
例如以下兩者是一樣的:

$pattern = '[ABC]';
$pattern = 'A|B|C';

不過 Character Class 不能表示超過一個字元的字串。

例:
$st = '11AB22KLMN33PQRS';
$pattern = 'AB|KLM|PQRST';

會找到以下的 match:
0 : AB
1 : KLM

如果寫:
$pattern = 'PQRST|KLM|AB';

那還是會先找到 'AB',效果跟 Character Class 一樣。

------------------------------------------------------------------------------------------------

[[群組 (Group)]]

如果想表示「'ABC'或者'ABKL'或者'ABPQR'」,除了可以寫:
$pattern = 'ABC|ABKL|ABPQR';

還可以這樣寫:
$pattern = 'AB(C|KL|PQR)';

即用括號來分開'AB'和接著的那三個 Alternatives,
好像數學上把 2*3 + 2*4 + 2*5 寫成 2 * (3+4+5)。

如果想連'AB'都要 match:
$pattern = 'AB|ABC|ABKL|ABPQR';

也可以把空字串都寫到括號內:
$pattern = 'AB(C|KL|PQR|)';

------------------------------------------------------------------------------------------------

[[群組 - 特殊變數 $1, $2, ...]]

括號還有一種特別的功能,就是把括號內 match 到的部份
放到特殊變數 $1, $2, ...。

例如您想檢查某字串是含有時間的模式,例如 HH:MM:SS,
(不檢查數值範圍),同時想取得時分秒各個部份,可以寫:

####
my ($time_string, $hour, $minute, $second);

$time_string = '12:34:56';
$pattern = '(\d\d):(\d\d):(\d\d)';

if ($time_string =~ /$pattern/) {
($hour, $minute, $second) = ($1, $2, $3);
print "hour=$hour,minute=$minute,second=$second";
} else {
print 'Invalid time format';
}

####

執行結果:
hour=12,minute=34,second=56


至於哪部份是 $1, $2, ...,就以開括號出現的先後順序來決定,
例如括著「時」那個開括號是第一個出現的,所以它就對應 $1。

(\d\d):(\d\d):(\d\d)
1______2______3

------------------------------------------------------------------------------------------------

[[群組 - 巢狀群組]]

括號內也可以有括號,成為巢狀群組,例如:

(\d\d):((\d\d):(\d\d))
1______23______4

####
my ($time_string, $hour, $minute, $second);

$time_string = '12:34:56';
$pattern = '(\d\d):((\d\d):(\d\d))';

if ($time_string =~ /$pattern/) {
print "\$1=$1\n\$2=$2\n\$3=$3\n\$4=$4";
} else {
print 'Invalid time format';
}
####

執行結果:
$1=12
$2=34:56
$3=34
$4=56

------------------------------------------------------------------------------------------------

[[群組 - 返回參照 (Backreferences) \1, \2, ...]]


跟 $1, $2, ... 有很密切關係的東西,就是 Backreferences,
即 \1, \2, ... 。

它們不同之處,在於 $1 只應該用在 regexp 「以外」,
而 \1 只應該用在 regexp 「以內」。

例:
####
$st = 'A_A';
$pattern = '([ABC])_\1';

if ($st =~ /$pattern/) {
print "\$1=$1";
}
####

執行結果:
$1=A


您可以嘗試把 $st 改成:
$st = 'B_B';
$st = 'C_C';

您會發現 \1 能夠取得之前所 match 的東西,
即應該放到 $1 的值。但要緊記,不應該在 regexp 以內寫 $1。


例:
####
$st = 'ALZ_A_L_Z';
$pattern = '([ABC])([KLM])([XYZ])_\1_\2_\3';

if ($st =~ /$pattern/) {
print "\$1=$1\n\$2=$2\n\$3=$3";
}
####

執行結果:
$1=A
$2=L
$3=Z

------------------------------------------------------------------------------------------------

[[ 群組 - $1, $2, ... 的開始和結束位置 ]]

Perl 5.6.0 提供了兩個陣列 @- 和 @+,
@- 儲存了 $1, $2, ... 的開始位置,@+ 就儲存結束位置。

例:
####
my ($time_string, $hour, $minute, $second);

$time_string = '12:34:56';
$pattern = '(\d\d):(\d\d):(\d\d)';

$time_string =~ /$pattern/;

foreach my $i (1 .. $#-) {
print "Match $i='${$i}' at position ($-[$i] , $+[$i])\n";
}
####

執行結果:
Match 1='12' at position (0 , 2)
Match 2='34' at position (3 , 5)
Match 3='56' at position (6 , 8)

------------------------------------------------------------------------------------------------

[[ 群組 - $` 、 $& 和 $' ]]

如果 regexp 沒有寫括號,就沒有 $1, $2, ... 可用了,
但您仍然可以用 $& 來取得 match,$` 則取得 match 前面所有東西,
$' 取得 match 後面所有東西。


####
$st = '11AA22';
$pattern = 'AA';

$st =~ /$pattern/;

print "\$`=$`\n\$&=$&\n\$'=$'";
####

執行結果:
$`=11
$&=AA
$'=22


如果用 substr 來表示,那麼:

$` 是 substr( $st, 0, $-[0] )
$& 是 substr( $st, $-[0], $+[0] - $-[0] )
$' 是 substr( $st, $+[0] )

------------------------------------------------------------------------------------------------

[[ 重覆 (Repetition) ]]

如果想表示「五個 'A' 字元」,可以寫
$pattern = 'AAAAA';

也可以用量化詞 (Quantifier) ,寫成
$pattern = 'A{5}';

這樣就更清楚了。而量化詞可以寫在字元、字元類別或群組的後面。

例:
$st = '11AAA22AAAAA';
$pattern = 'A{5}';

------------------------------------------------------------------------------------------------

[[重覆 - 最多和最少]]

量化詞可分成兩類:
1) 最多配對(貪心的) Maximal Match (Greedy)
2) 最少配對(不貪心的) Minimal Match (Non-greedy)


以下列出「最多配對」:

A? 表示 0 個或 1 個 'A' 字元(以最多為準)
A* 表示 0 個或以上的 'A' 字元(以最多為準)
A+ 表示 1 個或以上的 'A' 字元(以最多為準)

A{n} 表示 n 個 'A' 字元
A{n,m} 表示最少 n 個,最多 m 個 'A' 字元(以最多為準)
A{n,} 表示最少 n 個 'A' 字元(以最多為準)

以下列出「最少配對」:

A?? 表示 0 個或 1 個 'A' 字元(以最少為準)
A*? 表示 0 個或以上的 'A' 字元(以最少為準)
A+? 表示 1 個或以上的 'A' 字元(以最少為準)

A{n}? 表示 n 個 'A' 字元
(這個其實沒有所謂的「最多」還是「最少」之分,
定義這個符號只是為了保持符號一致性而己)
A{n, m}? 表示最少 n 個,最多 m 個 'A' 字元(以最少為準)
A{n,}? 表示最少 n 個 'A' 字元(以最少為準)


到底「最多配對」和「最少配對」有什麼分別呢?例如

$st = 'AAAAA';
$pattern1 = 'A{3,5}'; # 最多配對
$pattern2 = 'A{3,5}?'; # 最少配對

那麼 match 可能是:
1) 'AAA'(字元位置0至2)
2) 'AAAA'(字元位置0至3)
3) 'AAAAA'(字元位置0至4)

如果是「最多配對」,那麼 match 就是 'AAAAA',因為它是最多次數,
如果是「最少配對」,那麼 match 就是 'AAA',因為它是最少次數。

------------------------------------------------------------------------------------------------

[[ 特點字元 - 任何一個字元 ]]

如果要表示「任何一個字元」,可以用點號 '.'。

在預設的情況下,它會配到「任何一個字元,除了 "\n" 之外」:

$st = "123ABC_!@\n";
$pattern = '.';

@matches = $st =~ /$pattern/g;

如果要配到「任何一個字元,包括 "\n"」,
就到加入 s 這個「修飾詞」(Modifier):

@matches = $st =~ /$pattern/sg;

s 的意思是「把字串當作單行 (Single Line)」。

------------------------------------------------------------------------------------------------

[[ 字串的開頭 ]]

如果要表示「字串的開頭」,可以用 '^',例如:

$st = 'AA22';
$pattern = '^AA';

@matches = $st =~ /$pattern/g;


執行結果:
0 : AA


但 match 當中不會有 '^' 所配出來的字元,
因為 '^' 主要用來測試字串的特性(例如字串位置),
而不是配任何字元的。

為了方便理解,您可以把它「想像」成一個看得見的字元,
例如:

$st = '頭AA22尾'; ## $st = 'AA22'
$pattern = '頭AA'; ## $pattern = '^AA';

------------------------------------------------------------------------------------------------

[[ 字串的結尾 ]]

如果要表示「字串的結尾,或者在字串最尾的 \n 和前面字元的中間」,
可以用 '$',例如:

$st = "AA\nBB\n"; ## 用了雙引號
$pattern = '$';

@matches = $st =~ /$pattern/g;

結果有兩個 match ,但是'$' 與 '^' 一樣,是不會配到字元的。
您可以把它想像成:

$st = "頭AA\nBB尾\n尾"; ## $st = "AA\nBB\n";
$pattern = '尾'; ## $pattern = '$';

另外,當檢查一個字串是否符合某種格式時,
通常會 '^' 和 '$' 一起用,
例如檢查某字串是否符合「三位數字」的格式:

####
$st = "123";
$pattern = '^\d{3}$';

if ($st =~ /$pattern/) {
print "Valid format";
} else {
print "Invalid format";
}
####

------------------------------------------------------------------------------------------------

[[ 多行 (Multi-line) ]]

一個字串內可以含有 \n,例如:

$st = "AA\nAA\nAA\n";
$pattern = '^AA';

如果我們想 '^' 配到每一行的開頭,
可以用 m 修飾詞:

@matches = $st =~ /$pattern/mg;

執行結果:
0 : AA
1 : AA
2 : AA


想像成:

$st = "頭AA尾\n頭AA尾\n頭AA尾\n尾";
$pattern = '頭AA';


用了 m 修飾詞,也會令 '$' 配到每一行的結尾,


$st = "AA\nAA\nAA\n";
$pattern = 'AA$';
@matches = $st =~ /$pattern/mg;


想像成:

$st = "頭AA尾\n頭AA尾\n頭AA尾\n尾";
$pattern = 'AA尾';

如果用了 m 修飾詞,您仍然可以用:

\A 表示沒有 m 修飾詞的 '^' 意思
\Z 表示沒有 m 修飾詞的 '$' 意思
\z 代表字串的結尾(即 \Z ,但不包括最尾 \n 和前面字元的中間)


$st = "AA\nAA\nAA\n";
$pattern1 = '\AAA';
$pattern2 = 'AA\Z';
$pattern3 = 'AA\z';


總括來說,s 修飾詞會影響 '.' 的意思,
m 修飾詞會影響 '^' 和 '$' 的意思,
s 和 m 總共有四種可能:

沒有 s 和 m :預設模式 (Default Mode)
/s :單行模式 (Single Line Mode)
/m :多行模式 (Multi-Line Mode)
/sm :清楚多行模式 (Clean Multi-Line Mode)


看看大家能否解釋以下程式的執行結果:

####
$st = "A\nA\nA\n";
$pattern = '(^|$|.)';

@matches = $st =~ /$pattern/smg;
####

------------------------------------------------------------------------------------------------

[[ 字的邊界 Word Boundary ]]

字的邊界是指兩個連續字元的中間,
一個會配到 \w,而另一個會配到 \W。
(字串的開頭和結尾也視為配到 \W)

例如:

$st = 'ABC-=+';
$pattern = 'C\b';

@matches = $st =~ /$pattern/g;

因為左邊的 'C' 會配到 \w,而右邊的 '-' 會配到 \W,
所以 \b 會配到它們的中間。

\b 不代表某個字元,而是代表某個位置的特性。
您可以想像成:

$st = '界ABC界-=+';
$pattern = 'C界';

您可以用 \B 來表示 \b 的相反,即「不是邊界」,

例如:

$st = 'ABCDE';
$pattern = '\BC\B';

@matches = $st =~ /$pattern/g;

因為 'C' 本身配到 \w ,而它左邊的'B'和右邊的'D'也是配到 \w,
所以沒有邊界。

------------------------------------------------------------------------------------------------

[[ 不分大小寫 (Case-insensitive) ]]

如果加入了 i 修飾詞,就會做不分大小寫的配對,
例如:

$st = '11AA22BB';
$pattern = '[abAB]';

@matches = $st =~ /$pattern/g;

可以用 i 修飾詞寫成:

$st = '11AA22BB';
$pattern = '[AB]'; # 或者 '[ab]'

@matches = $st =~ /$pattern/ig;

因為不分大小寫,所以不必在 $pattern 把大小寫都列出來了。

------------------------------------------------------------------------------------------------

[[ x 修飾詞 ]]

如果用了 x 修飾詞,就會令 regexp 解譯器忽略 $pattern 內的白格,
(除了那些在被反斜號標示了的白格,及那些在字元類別內的白格)
還有在 $pattern 內的 '#' 也會有好像 Perl 一樣的單行註解作用,
(除了那些在被反斜號標示了的'#',及那些在字元類別內的'#')

它的用途是讓您可以把 regexp 寫成容易閱讀的方式,及加入註解。
例子:


不用 x 修飾詞的寫法:

$st = '11AA22 33\\\\44##';
$pattern = '(A| |\\\\|#)';
@matches = $st =~ /$pattern/g;


用了 x 修飾詞的寫法:

$st = '11AA22 33\\\\44##';

$pattern = q{

( # 左括號表示 Alternation 開始
A # 'A' 字元
| # 或者
[ ] # 用字元類別表示空格
| # 或者
\\\\ # 反斜號字元
|
[#] # 用字元類別表示'#'
) # 右括號表示完結

};

@matches = $st =~ /$pattern/xg;

------------------------------------------------------------------------------------------------

[[ 伸展模式 (Extended Patterns) ]]

除了傳統的 regexp 語法外,Perl 還定義了一些額外的語法,
但在其它工具未必有這些語法,而且有些語法可能還在實驗階段,
說不定將來會被刪除,詳情請參閱說明文件。

這些語法都是這樣子的:

(?char)

而 char 就表示伸展的種類。

------------------------------------------------------------------------------------------------

[[ 伸展模式 - 內嵌註解 (Embedding Comments) ]]

語法:
(?#text)

在 text 位置的東西會當作註解,
但要留意 text 內不可以含有右括號。

例:
$pattern = '(?# THIS IS A COMMENT)(A| |\\\\|#)';

------------------------------------------------------------------------------------------------

[[ 伸展模式 - 非捕捉群組 (Non-capturing groupings) ]]

語法:
(?:pattern)

之前提到括號有個特別的作用,就是會把配到的東西放進 $1, $2,...,
例如:

$time_string = '12:34:56';
$pattern = '(\d\d):(\d\d):(\d\d)';

if ($time_string =~ /$pattern/) {
($hour, $minute, $second) = ($1, $2, $3);
print "hour=$hour,minute=$minute,second=$second";
}

但如果您不想放的話,可以用 (?:pattern):

$time_string = '12:34:56';
$pattern = '(\d\d):(?:\d\d):(\d\d)';

if ($time_string =~ /$pattern/) {
($hour, $minute, $second) = ($1, $2, $3);
print "hour=$hour,minute=$minute,second=$second";
}


因為「分」那部份使用了(?:pattern) ,所以就不會放到本來的 $2,
這就輪到「秒」的部份放到 $2了。

------------------------------------------------------------------------------------------------

[[ 伸展模式 - 內嵌修飾詞 (Embedded Modifiers) ]]

語法:
(?imsx-imsx:pattern)

之前提到可以用 i 修飾詞來做不分大小寫的配對,例如:

$st = '11AB22Ab33aB44ab';
$pattern = 'AB';

@matches = $st =~ /$pattern/ig;

這樣整個 $pattern 都是不分大小寫了,
如果想某部份分辨大小寫,某部份不分辨,
可以用內嵌的寫法。

$st = '11AB22Ab33aB44ab';
$pattern = '(?i:A)(?-i:B)';

@matches = $st =~ /$pattern/g;

表示 A 不分大小寫,B 就分辨大小寫。
如果修飾詞前面是減號,表示關閉該修飾詞,
例如 (?s-i),表示啟動 s 修飾詞,關閉 i 修飾詞。

其實內嵌修飾詞的語法,是在非捕捉群組內加入修飾詞而己。

------------------------------------------------------------------------------------------------

[[ 伸展模式 - 往前看(Look-ahead)和往後看(Look-behind) ]]

語法:
往前看(正)(Positive Look-ahead) (?=pattern)
往後看(正)(Positive Look-behind) (?<=pattern)
往前看(負)(Negative Look-ahead) (?!pattern)
往後看(負)(Negative Look-behind) (?
舉例來說:

$st = 'ABCDEFGH';
$pattern = '\w{2}';
@matches = $st =~ /$pattern/g;


執行結果:
0 : AB
1 : CD
2 : EF
3 : GH

您可以想像它是由左往右走,當配到一個match,
下一次就由match之後開始走:

ABCDEFGH
由A的位置開始走,配到 AB,剩下 CDEFGH ('AB')
由C的位置開始走,配到 CD,剩下 EFGH ('AB', 'CD')
由E的位置開始走,配到 EF,剩下 GH ('AB', 'CD', 'EF')
由G的位置開始走,配到 GH,剩下 ('AB', 'CD', 'EF', 'GH')


如果想在走路的時候,還要同時往前面看,
可以用「往前看」或「往後看」。
例如仍然要找出 \w{2},但同時要檢查它的前面(即右面)必須是\w{2},
可以寫:

$pattern = '\w{2}(?=\w{2})';

執行結果:
0 : AB
1 : CD
2 : EF


這裡的 (?=\w{2}) 用了「往前看(正)」,
這部份是不會配到任何字元,只是用來檢查字串的特性:

ABCDEFGH
由A的位置開始,配到AB,往前看到CD,剩下 CDEFGH ('AB')
由C的位置開始,配到CD,往前看到EF,剩下 EFGH ('AB', 'CD')
由E的位置開始,配到EF,往前看到GH,剩下 GH ('AB', 'CD', 'EF')
由G的位置開始,雖然GH 可以配到\w{2} ,但往前看不到 \w{2},所以沒有match
完結

同理,如果改用「往後看」:

$pattern = '(?<=\w{2})\w{2}';

執行結果:
0 : CD
1 : EF
2 : GH

ABCDEFGH
由A的位置開始,直至走到C的位置時,
才能夠往後看到 AB,配到CD,剩下 EFGH ('CD')
由E的位置開始,往後看到CD,配到EF,剩下 GH ('CD', 'EF')
由G的位置開始,往後看到EF,配到GH,剩下 ('CD', 'EF', 'GH')
完結


Negative Look-ahead 是 Positve Look-ahead的相反,
例如仍然要找出 \w{2},但同時要檢查它的前面必須「不是」\w{2},
可以寫:

$st = 'ABCDEFGH';
$pattern = '\w{2}(?!\w{2})';
@matches = $st =~ /$pattern/g;


執行結果:
0 : FG

因為 'FG'的右面只有 'H' ,即 \w ,而不是 \w{2},
所以配到。