PHP程式設計問題諮詢:
要如何使Html/PHP格式的字串不被解釋,而是照原樣顯示?
———————————————————
PHP程式設計問題回覆:
使Html/PHP格式的字串不被解釋,你可以這樣處理:
<?PHP
$str=”<h1>PHP</h1>”;
Echo “被解釋過的: “.$str.”<br>經過處理的:”;
Echo htmlentities(nl2br($str));
?>
———————————————————
PHP程式設計教學
PHP程式設計問題諮詢:
要如何使Html/PHP格式的字串不被解釋,而是照原樣顯示?
———————————————————
PHP程式設計問題回覆:
使Html/PHP格式的字串不被解釋,你可以這樣處理:
<?PHP
$str=”<h1>PHP</h1>”;
Echo “被解釋過的: “.$str.”<br>經過處理的:”;
Echo htmlentities(nl2br($str));
?>
———————————————————
常用的php正則表達式及語法註解:
符合中文字元的正則表達式: [u4e00-u9fa5]
註解:符合中文還真是個頭疼的事,有了這個表達式就好辦了
符合雙位元組字元(內含中文字在內):[^x00-xff]
註解:可以用來計算字串的長度(一個雙位元組字元長度計2,ASCII字元計1)
符合空白行的正則表達式:
s*
註解:可以用來刪除空白行
符合 HTML標示的正則表達式:<(S*?)[^>]*>.*?|<.*? />
註解:網上流傳的版本太糟糕,上面這個也僅僅能符合部分,對於複雜的嵌套標示依舊無能為力
符合首尾空白字元的正則表達式:^s*|s*$
註解:可以用來刪除行首行尾的空白字元(內含空格、製表符、換頁符等等),非常有用的表達式
符合Email位址的正則表達式:w+ ([-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*
註解:表單驗證時很實用
符合網址URL的正則表達式:[a-zA-z]+://[^s]*
註解:網上流傳的版本功能很有限,上面這個基本可以滿足需求
符合帳號是否合法 (字母開頭,容許5-16位元組,容許字母數字下劃線):^[a-zA-Z][a-zA-Z0-9_]{4,15}$
註解:表單驗證時很實用
符合電話號碼:d{3}-d{8}|d{4}-d{7}
註解:符合形式如 0511-4405222 或 021-87888822
符合騰訊QQ號:[1-9][0-9]{4,}
註解:騰訊QQ號從10000開始
符合中國郵政編碼:[1-9]d{5}(?! d)
註解:中國郵政編碼為6位數字
符合身份證:d{15}|d{18}
註解:中國的身份證為15位或18位
符合ip位址:d+.d+.d+.d+
註解:提取ip位址時有用
符合特定數字:
^[1-9]d*$ //符合正整數
^-[1-9]d*$ //符合負整數
^-?[1-9]d*$ //符合整數
^[1-9]d*|0$ //符合非負整數(正整數 + 0)
^-[1-9]d*|0$ //符合非正整數(負整數 + 0)
^[1-9]d*.d*|0.d*[1-9]d*$ //符合正浮點數
^-([1-9]d*.d*|0.d*[1-9]d*)$ //符合負浮點數
^-?([1-9]d*.d*|0.d*[1-9]d*|0?.0+|0)$ //符合浮點數
^[1-9]d*.d*|0.d*[1-9]d*|0?.0+|0$ //符合非負浮點數(正浮點數 + 0)
^(-([1-9]d*.d*|0.d*[1-9]d*))|0?.0+|0$ //符合非正浮點數(負浮點數 + 0)
註解:處理大量資料時有用,具體應用時注意修正
符合特定字串:
^[A-Za-z]+$ //符合由26個英文字母組成的字串
^[A-Z]+$ //符合由26個英文字母的大寫組成的字串
^[a-z]+$ //符合由26個英文字母的小寫組成的字串
^[A-Za-z0-9]+$ //符合由數字和26個英文字母組成的字串
^w+$ //符合由數字、26個英文字母或是下劃線組成的字串
註解:最基本也是最常用的一些表達式
解決:執行程式發生記憶體不足解決方法 Fatal error
網頁開啟後出現這樣的字串:Fatal error: Allowed memory size of 16789338 bytes exhausted
這是因為:php 執行程式發生記憶體不足解決方法 Fatal error: Allowed memory size of 16789338 bytes exhausted
在php.ini 文件裡把memory_limit = 16M這個數值調大,例如修改成32M或更大。
今天總教頭在此提供一個更好的解決方法,只要直接在php程式中設定
(當然:記憶體大小可視需求,記得要放在整個程式的前面)就可以了
<?php
ini_set(‘memory_limit’,’128m’);
?>
這個方法是在php程式上動態設定memory,不會動到PHP的全域變數設定。
馬上可以解決執行程式發生記憶體不足解決方法 Fatal error……………
類建立好了..
那麼類肯定不止這些東西,它由繼承,屬性,返回值等.
1.方法的參數.
有方法,該方法就可能要有參數.參數是如何傳遞過來的呢?
如果看過之前的文章.那麼你肯定就知道了..是的 與普通函數的參數相同.
聲明char類型的指標用來儲存參數的值.
聲明int類型的變量來儲存的參數長度
然後用到 zend_parse_parameters函數
zend_parse_parameters(“,`< char *type_spec>`,`< …>`)
該參數有幾個重要的參數
第一個是參數的個數
第二個比較重要,它指定接收參數的類型
下面這份清單完整地列舉出了我們可以指定接收的參數類型:
l – 長整數
d – 雙精度浮點數
s – 字串 (也可能是空位元組)和其長度
b – 布林值
r – 資源, 儲存在 zval*
a – 陣列, 儲存在 zval*
o – (任何類的)對像, 儲存在 zval*
O – (由class entry 特殊的類的)對像, 儲存在 zval*
z – 實際的 zval*
下面的一些字元在類型說明字串(就是那個 char *type_spec)中具有特別的含義:
| – 顯示剩下的參數都是可選參數。如果使用者沒有傳進來這些參數值,那麼這些值就會被起始化成預設值。
/ – 顯示參數解析函數將會對剩下的參數以 SEPARATE_ZVAL_IF_NOT_REF() 的模式來提供這個參數的一份覆制,除非這些參數是一個引用。
! – 顯示剩下的參數容許被設定為 NULL(僅用在 a、o、O、r和z身上)。如果使用者傳進來了一個 NULL 值,則存儲該參數的變量將會設定為 NULL。
函數 get接收兩個參數 ,第一個參數是必填字串類型,第二個參數是可選為整型.
PHP代碼如下
1
2
3
function get($name,$age=0){
}
如果要在延伸裡實現這樣的功能,zend_parse_parameters的使用如下
char* name;
int name_length;
int age;
int age_length;
zend_parse_parameters(ZEND_NUM_ARGS ,」s|l」,&name,&name_length,&age,&age_len)
zend_parse_parameters 執行失敗的話會返回FAILURE.
執行完成後,name就是第一個參數的值,age就是第二個參數的值
我們甚至可以呼叫php_printf函數來輸出.
2.類的屬性
這類的內容在上一篇文章中其實已經講清楚了.
用zend_declare_property_*一系列函數來建立我們的屬性.當然是在PHP_MINIT_FUNCTION函數中建立
以 zend_declare_property_null為例:
zend_declare_property_null(“,`< char *name>`,`< int name_length>`,`< int access_type TSRMLS_DC>`)
ce :是一個zend_class_entry的指標.
name :是屬性的名稱
length :屬性名稱的長度
access_type:屬性的訪問層級.
還有其他幾個函數 用法一樣.
1
2
3
4
5
6
7
zend_declare_property_bool
zend_declare_property_double
zend_declare_property_ex
zend_declare_property_long
zend_declare_property_null
zend_declare_property_string
zend_declare_property_stringl
3.讀取屬性
建立了屬性,那麼該如何讀取此屬性呢?
zend_read_property(“,`< zval *object>`,`< char *name>`,`< int name_length>`,`< zend_bool silent TSRMLS_DC>`);
就要使用這個函數了,它將取得到的屬性訊息返回到一個zval結構體中
這時候要用到函數getThis();
這個函數會返回一個zval的指標.
因為是一個對像 所以儲存在結構體_zend_object_value 中
typedef struct _zend_object_value {
zend_object_handle handle;
zend_object_handlers *handlers;
} zend_object_value;
第一個參數會呼叫 zend_object_handler所指向的函數指標get_class_entry來取得現用的執行的類的訊息.
第一個參數應該是 Z_OBJEC_P(getThis());
第二個參數是 getThis();
它定義為 #define getThis() (this_ptr);
this_ptr是什麼東西我還不確定,我想應該是PHP內核維護的一個儲存類訊息的zval對像.回頭再好好研究研究.
第三個參數是要取得的屬性
第四個參數是屬性的長度.
修改之後的getproperty方法
1
2
3
4
5
6
PHP_METHOD(Person,getproperty){
zval* self=getThis();
zval* name;
name=zend_read_property(Z_OBJCE_P(self),self,ZEND_STRL("name"),0 TSRMLS_CC);
php_printf("%s",name->value.str.val);
}
4.設定屬性
該如何更新屬性的值呢?
同樣給我們提供了一個函數
zend_update_property(“,`< zval *object>`,`< char *name>`,`< int name_length>`,`< zval *value TSRMLS_DC>`)
前四個參數與zend_read_property相同.
不同的是最後一個zval*value.它是我們屬性的值..因為他是一個zval的指標,所以我們需要將通過zend_parse_parameters取得到的參數轉為一個zval的指標.
修改後的setproperty方法.
1
2
3
4
5
6
7
8
9
10
11
12
13
PHP_METHOD(Person,setproperty){
char *key=NULL;
int key_len;
char *val=NULL;
int val_len;
zval* self=getThis();
zval* value;
if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"ss",&key,&key_len,&val,&val_len)==FAILURE){
return;
}
MAKE_STD_ZVAL(value);
ZVAL_STRINGL(value,val,val_len,1);
zend_update_property(Z_OBJCE_P(self),self,key,key_len,value TSRMLS_CC);
最後make && make install,重啟apache.
這樣我們在 php代碼裡.
1
2
3
4
5
<?php
$a=new Person();
$a->setproperty("name","this is siren");
$a->getproperty();
?>
就可以輸出我們通過setproperty設定的值了.
聲明:本文為斯人原創,全部為作者一一分析得之,有不對的地方望賜教。
歡迎轉載,轉載請註明出處 。
本文位址:http://imsiren.com/archives/581
PHP程式設計問題諮詢:
如何比較兩個日期相差幾天?
———————————————————
PHP程式設計問題回覆:
要如何比較兩個日期相差幾天,請看以下的範例:
<?PHP
$Date_1="2003-7-15";//也可以是:$Date_1="2003-6-25 23:29:14";
$Date_2="1982-10-1";
$Date_List_1=explode("-",$Date_1);
$Date_List_2=explode("-",$Date_2);
$d1=mktime(0,0,0,$Date_List_1[1],$Date_List_1[2],$Date_List_1[0]);
$d2=mktime(0,0,0,$Date_List_2[1],$Date_List_2[2],$Date_List_2[0]);
$Days=round(($d1-$d2)/3600/24);
Echo "我已經奮鬥了 $Days 天^_^";
?>
———————————————————
PHP網站如何防止CC攻擊呢?
所謂的CC攻擊就是對方利用程式或一些代理對您的網站進行不間斷的訪問,造成您的網站處理不了而處於當機狀態。
這種時候您的統計系統(可能是量子、百度等)當然也是統計不到的。
不過我們可以借助於一些防攻擊的軟體來實現,不過效果有時並不明顯。
下面我提供一段PHP的代碼,可以起到一定的防CC效果。
主要功能:在3秒內連續重整頁面5次以上將指向本機 http://127.0.0.1
在PHP網站開發中為了網站推廣和SEO等需要,需要對網站進行全站或局部靜態化處理,PHP生成靜態HTML頁面有多種方法
例如:利用PHP範本、快取等實現頁面靜態化
今天就以PHP案例教學形式討論PHP生成靜態頁面的方法。
頁面靜態化的方法,分為兩種
一種是偽靜態,就是url 重寫
一種是你真的靜態化。
下面介紹PHP中頁面靜態化的方法。
什麼是PHP程式網頁靜態化?
PHP程式網頁靜態化的簡單理解就是使網站生成頁面以靜態HTML的形式展現在訪客面前
PHP靜態化分純靜態化和偽靜態化,兩者的區別在於PHP生成靜態頁面的處理機制不同。
PHP程式網頁生成靜態HTML頁面的方法:
1、利用PHP範本生成靜態頁面
PHP範本實現靜態化非常方便,比如安裝和使用PHP Smarty實現網站靜態化。
2、使用PHP檔案讀寫功能生成靜態頁面
PHP生成靜態頁面案例代碼
<?
$out1 = “<html><head><title>PHP程式網頁靜態化教學</title></head>
<body>歡迎訪問PHP網站開發教學網http://por.tw,本文主要介紹PHP網站頁面靜態化的方法
</body></html>”;
$fp = fopen(“leapsoulcn.html”,”w”);
if(!$fp)
{
echo “System Error”;
exit();
}
else {
fwrite($fp,$out1);
fclose($fp);
echo “Success”;
}
?>
———————————————————————–
3、使用PHP輸出控制函數(Output Control)生成靜態頁面
輸出控制函數(Output Control)也就是使用和控制快取來生成靜態HTML頁面,也會使用到PHP檔案讀寫函數。
PHP生成靜態頁面案例代碼
———————————————————————–
<?
ob_start();
echo “<html>”.
“<head>”.
“<title>PHP網站靜態化教學</title>”.
“</head>”.
“<body>歡迎訪問PHP網站開發教學網http://por.tw,本文主要介紹PHP網站頁面靜態化的方法</body>”.
“</html>”;
$out1 = ob_get_contents();
ob_end_clean();
$fp = fopen(“leapsoulcn.html”,”w”);
if(!$fp)
{
echo “System Error”;
exit();
}
else
{
fwrite($fp,$out1);
fclose($fp);
echo “Success”;
}
?>
———————————————————————
我們知道使用PHP進行網站開發,一般執行結果直接輸出到遊覽器,為了使用PHP生成靜態頁面,就需要使用輸出控制函數控制快取區,以便取得快取區的內容,然後再輸出到靜態HTML頁面檔案中以實現網站靜態化。
PHP生成靜態頁面的思路為:
首先開啟快取,然後輸出了HTML內容(你也可以通過include將HTML內容以檔案形式包括進來),之後取得快取中的內容,清理快取後通過PHP檔案讀寫函數將快取內容寫入到靜態HTML頁面檔案中。
獲得輸出的快取內容以生成靜態HTML頁面的過程需要使用三個函數:
ob_start()、ob_get_contents()、ob_end_clean()。
PHP程式網頁生成靜態頁面知識:
1、ob_start函數一般主要是用來開啟快取,注意使用ob_start之前不能有任何輸出,如空格、字元等。
2、ob_get_contents函數主要用來取得快取中的內容以字串形式返回,注意此函數必須在ob_end_clean函數之前呼叫,否則取得不到快取內容。
3、ob_end_clean函數主要是清理快取中的內容並關閉快取,完成則返回True,失敗則返回False
PHP輸出控制函數(Output Control)有很多應用。
至此,使用PHP生成靜態HTML頁面以實現網站靜態化的方法就介紹完了,根據實際情況和需求你可以選取不同的靜態化方法。
您知道最短的PHP微網誌程式代碼如何編寫嗎?
最短的PHP微網誌程式。
基本需求是:
1.無亂碼(最好UTF-8)。
2.有輸入框可以發佈新訊息。
3.送出後馬上可以看到新發的內容。
4.必須使用POST模式送出。
5.訊息後面要有時間標示。
6.不能發佈任何HTML標籤。
<?php
header("content-type:text/html; charset=utf-8");
$a=@file_get_contents(l);
($p=$_POST[s])&&file_put_contents(l,$a='<hr>'.htmlspecialchars($p).date(' Y-m-d H:i').$a);
echo '<form method=post><input name=s></form>'.$a;
?>
運行效果如圖:
註釋一下:
<?php
header("content-type:text/html; charset=utf-8"); //傳送到utf-8聲明
/*
* 從名叫l的檔案裡面讀取資料。
* php裡面的常量如果沒有先聲明,那麼預設值是等於常量本身的那個字串。
* 也就是說沒有聲明過的l=="l" , ABCD == "ABCD"
*/
$a=@file_get_contents(l);
/*
* a && b; 貌似是php特有的語法結構。 大概意思是 先執行a,
* 然後判斷a是否為true,如果是,那麼繼續執行b
*
* ($p=$_POST[s]) 是先把$_POST[s]的值賦給$p,
* 然後括號的值為$p轉換為布爾型。也就是說,只要$p不是空字串,
* 或是false,或是0,或是null。就會繼續執行後面的語句
*
* 後面操作是把剛剛讀出來的$a的前面加上新的資料,然後寫入l檔案
*/
($p=$_POST[s])&&file_put_contents(l,$a='<hr>'.htmlspecialchars($p).date(' Y-m-d H:i').$a);
/*
* 顯示html語句,用於構造一個可以post送出的輸入框,沒有送出按鈕,直接按換行送出。
* 由於頁面是用utf-8編碼,所以送出的資料也是utf-8編碼
*/
echo '<form method=post><input name=s></form>'.$a;//#註釋2
?>
正則表達式是一個無比強大的工具.任何地方我們都可能用到它…
原來刻苦的啃過 正則表達式,但時間長了還是會忘…
今天又來溫習一下..看到一篇不錯的文章..所以就轉過來收藏下.
前言:
半年前我對正則表達式產生了興趣,在網上尋找過不少資料,看過不少的教學,最後在使用一個正則表達式工具RegexBuddy時發現他的教學寫的非常好,可以說是我目前見過最好的正則表達式教學。於是一直想把他翻譯過來。這個願望直到這個五一長假才得以實現,結果就有了這篇文章。關於本文的名字,使用「深入淺出」似乎已經太俗。但是通讀原文以後,覺得只有用「深入淺出」才能準確的表達出該教學給我的感受,所以也就不能免俗了。
本文是Jan Goyvaerts為RegexBuddy寫的教學的譯文,版權歸原作者所有,歡迎轉載。但是為了尊重原作者和譯者的勞動,請註明出處!謝謝!
1.什麼是正則表達式
基本說來,正則表達式是一種用來描述一定數量文字的模式。Regex代表Regular Express。本文將用<<regex>>來表示一段具體的正則表達式。
一段文字就是最基本的模式,簡單的符合相同的文字。
2.不同的正則表達式引擎
正則表達式引擎是一種可以處理正則表達式的軟體。通常,引擎是更大的應用程式的一部分。在軟體世界,不同的正則表達式並不互相相容。本教學會集中討論Perl 5 類型的引擎,因為這種引擎是應用最廣泛的引擎。同時我們也會提到一些和其他引擎的區別。許多近代的引擎都很類似,但不完全一樣。例如.NET正則庫,JDK正則包。
3.文字元號
最基本的正則表達式由單個文字元號組成。如<<a>>,它將符合字串中第一次出現的字元「a」。如對字串「Jack is a boy」。「J」後的「a」將被符合。而第二個「a」將不會被符合。
正則表達式也可以符合第二個「a」,這必須是你告訴正則表達式引擎從第一次符合的地方開始搜尋。在文字編輯器中,你可以使用「尋找下一個」。在寫程式語系中,會有一個函數可以使你從前一次符合的位置開始繼續向後搜尋。
類似的,<<cat>>會符合「About cats and dogs」中的「cat」。這等於是告訴正則表達式引擎,找到一個<<c>>,緊跟一個<<a>>,再跟一個<<t>>。
要注意,正則表達式引擎預設是大小寫敏感的。除非你告訴引擎忽略大小寫,否則<<cat>>不會符合「Cat」。
特殊字元
對於文字字元,有12個字元被保留作特殊用途。他們是:
[ ] ^ $ . | ? * + ( )
這些特殊字元也被稱作元字元。
如果你想在正則表達式中將這些字元用作文字字元,你需要用反斜槓「」對其進行換碼 (escape)。例如你想符合「1+1=2」,正確的表達式為<<1+1=2>>.
需要注意的是,<<1+1=2>>也是有效的正則表達式。但它不會符合「1+1=2」,而會符合「123+111=234」中的「111=2」。因為「+」在這裡表示特殊含義(重複1次到多次)。
在寫程式語系中,要注意,一些特殊的字元會先被編譯器處理,然後再傳遞給正則引擎。因此正則表達式<<1+2=2>>在C++中要寫成「1+1=2」。為了符合「C: emp」,你要用正則表達式<<C: emp>>。而在C++中,正則表達式則變成了「C: emp」。
不可顯示字元
可以使用特殊字元序列來代表某些不可顯示字元:
<< >>代表Tab(0×09)
<<
>>代表換行符(0x0D)
<<
>>代表換行符(0x0A)
要注意的是Windows中文字檔案使用「
」來結束一行而Unix使用「
」。
4.正則表達式引擎的內定工作機制
知道正則表達式引擎是如何工作的有助於你很快理解為何某個正則表達式不像你期望的那樣工作。
有兩種類型的引擎:文字導向(text-directed)的引擎和正則導向(regex-directed)的引擎。Jeffrey Friedl把他們稱作DFA和NFA引擎。本文談到的是正則導向的引擎。這是因為一些非常有用的特性,如「惰性」量詞(lazy quantifiers)和反向引用(backreferences),只能在正則導向的引擎中實現。所以毫不意外這種引擎是目前最流行的引擎。
你可以輕易分辨出所使用的引擎是文字導向還是正則導向。如果反向引用或「惰性」量詞被實現,則可以肯定你使用的引擎是正則導向的。你可以作如下測試:將正則表達式<<regex|regex not>>應用到字串「regex not」。如果符合的結果是regex,則引擎是正則導向的。如果結果是regex not,則是文字導向的。因為正則導向的引擎是「猴急」的,它會很急切的進行表功,報告它找到的第一個符合 。
正則導向的引擎總是返回最左邊的符合
這是需要你理解的很重要的一點:即使以後有可能發現一個「更好」的符合,正則導向的引擎也總是返回最左邊的符合。
當把<<cat>>應用到「He captured a catfish for his cat」,引擎先比較<<c>>和「H」,結果失敗了。於是引擎再比較<<c>>和「e」,也失敗了。直到第四個字元,<<c>>符合了「c」。<<a>>符合了第五個字元。到第六個字元<<t>>沒能符合「p」,也失敗了。引擎再繼續從第五個字元重新檢查符合性。直到第十五個字元開始,<<cat>>符合上了「catfish」中的「cat」,正則表達式引擎急切的返回第一個符合的結果,而不會再繼續尋找是否有其他更好的符合。
5.字集
字集是由一對方括號「[]」括起來的字集合。使用字集,你可以告訴正則表達式引擎僅僅符合多個字元中的一個。如果你想符合一個「a」或一個「e」,使用<<[ae]>>。你可以使用<<gr[ae]y>>符合gray或grey。這在你不確定你要搜尋的字元是採用美國英語還是英國英語時特別有用。相反,<<gr[ae]y>>將不會符合graay或graey。字集中的字元順序並沒有什麼關係,結果都是相同的。
你可以使用連字元「-」定義一個字元範圍作為字集。<<[0-9]>>符合0到9之間的單個數字。你可以使用不止一個範圍。<<[0-9a-fA-F] >>符合單個的十六進位數字,並且大小寫不敏感。你也可以結合範圍定義與單個字元定義。<<[0-9a-fxA-FX]>>符合一個十六進位數字或字母X。再次強調一下,字元和範圍定義的先後順序對結果沒有影響。
字集的一些應用
尋找一個可能有拼字錯誤的單詞,比如<<sep[ae]r[ae]te>> 或 <<li[cs]en[cs]e>>。
尋找程式語系的標識符,<<A-Za-z_][A-Za-z_0-9]*>>。(*表示重複0或多次)
尋找C風格的十六進位數<<0[xX][A-Fa-f0-9]+>>。(+表示重複一次或多次)
取反字集
在左方括號「[」後面緊跟一個尖括號「^」,將會對字集取反。結果是字集將符合任何不在方括號中的字元。不像「.」,取反字集是可以符合換行換行符的。
需要記住的很重要的一點是,取反字集必須要符合一個字元。<<q[^u]>>並不意味著:符合一個q,後面沒有u跟著。它意味著:符合一個q,後面跟著一個不是u的字元。所以它不會符合「Iraq」中的q,而會符合「Iraq is a country」中的q和一個空格符。事實上,空格符是符合中的一部分,因為它是一個「不是u的字元」。
如果你只想符合一個q,條件是q後面有一個不是u的字元,我們可以用後面將講到的向前檢視來解決。
字集中的元字元
需要注意的是,在字集中只有4個 字元具有特殊含義。它們是:「] ^ -」。「]」代表字集定義的結束;「」代表轉義;「^」代表取反;「-」代表範圍定義。其他常見的元字元在字集定義內定都是標準字元,不需要轉義。例如,要搜尋星號*或加號+,你可以用<<[+*]>>。當然,如果你對那些通常的元字元進行轉義,你的正則表達式一樣會工作得很好,但是這會降低可讀性。
在字集定義中為了將反斜槓「」作為一個文字字元而非特殊含義的字元,你需要用另一個反斜槓對它進行轉義。<<[x]>>將會符合一個反斜槓和一個X。「]^-」都可以用反斜槓進行轉義,或是將他們放在一個不可能使用到他們特殊含義的位置。我們推薦後者,因為這樣可以增加可讀性。比如對於字元「^」,將它放在除了左括號「[」後面的位置,使用的都是文字字元含義而非取反含義。如<<[x^]>>會符合一個x或^。<<[]x]>>會符合一個「]」或「x」。<<[-x]>>或<<[x-]>>都會符合一個「-」或「x」。
字集的簡寫
因為一些字集非常常用,所以有一些簡寫模式。
<<d>>代表<<[0-9]>>;
<<w>>代表單詞字元。這個是隨正則表達式實現的不同而有些差異。絕大多數的正則表達式實現的單詞字集都包括了<<A-Za-z0-9_]>>。
<<s>>代表「白字元」。這個也是和不同的實現有關的。在絕大多數的實現中,都包括了空格符和Tab符,以及換行換行符<<
>>。
字集的縮寫形式可以用在方括號之內或之外。<<sd>>符合一個白字元後面緊跟一個數字。<<[sd]>>符合單個白字元或數字。<<[da-fA-F]>>將符合一個十六進位數字。
取反字集的簡寫
<<[S]>> = <<[^s]>>
<<[W]>> = <<[^w]>>
<<[D]>> = <<[^d]>>
字集的重複
如果你用「?*+」操作符來重複一個字集,你將會重複整個字集。而不僅是它符合的那個字元。正則表達式<<[0-9]+>>會符合837以及222。
如果你僅僅想重複被符合的那個字元,可以用向後引用達到目的。我們以後將講到向後引用。
6.使用?*或+ 進行重複
?:告訴引擎符合前導字元0次或一次。事實上是表示前導字元是可選的。
+:告訴引擎符合前導字元1次或多次
*:告訴引擎符合前導字元0次或多次
<[A-Za-z][A-Za-z0-9]*>符合沒有屬性的HTML標籤,「<」以及「>」是文字元號。第一個字集符合一個字母,第二個字集符合一個字母或數字。
我們似乎也可以用<[A-Za-z0-9]+>。但是它會符合<1>。但是這個正則表達式在你知道你要搜尋的字串不包括類似的無效標籤時還是足夠有效的。
限制性重複
許多現代的正則表達式實現,都容許你定義對一個字元重複多少次。詞法是:{min,max}。min和max都是非負整數。如果逗號有而max被忽略了,則max沒有限制。如果逗號和max都被忽略了,則重複min次。
因此{0,}和*一樣,{1,}和+ 的作用一樣。
你可以用<<[1-9][0-9]{3}>>符合1000~9999之間的數字(「」表示單詞邊界)。<<[1-9][0-9]{2,4}>>符合一個在100~99999之間的數字。
注意貪婪性
假設你想用一個正則表達式符合一個HTML標籤。你知道輸入將會是一個有效的HTML檔案,因此正則表達式不需要排除那些無效的標籤。所以如果是在兩個尖括號之間的內容,就應該是一個HTML標籤。
許多正則表達式的新手會首先想到用正則表達式<< <.+> >>,他們會很驚訝的發現,對於測試字串,「This is a <EM>first</EM> test」,你可能期望會返回<EM>,然後繼續進行符合的時候,返回</EM>。
但事實是不會。正則表達式將會符合「<EM>first</EM>」。很顯然這不是我們想要的結果。原因在於「+」是貪婪的。也就是說,「+」會導致正則表達式引擎試圖盡可能的重複前導字元。只有當這種重複會引起整個正則表達式符合失敗的情況下,引擎會進行回溯。也就是說,它會放棄最後一次的「重複」,然後處理正則表達式餘下的部分。
和「+」類似,「?*」的重複也是貪婪的。
深入正則表達式引擎內定
讓我們來看看正則引擎如何符合前面的例子。第一個記號是「<」,這是一個文字元號。第二個符號是「.」,符合了字元「E」,然後「+」一直可以符合其餘的字元,直到一行的結束。然後到了換行符,符合失敗(「.」不符合換行符)。於是引擎開始對下一個正則表達式符號進行符合。也即試圖符合「>」。到目前為止,「<.+」已經符合了「<EM>first</EM> test」。引擎會試圖將「>」與換行符進行符合,結果失敗了。於是引擎進行回溯。結果是現在「<.+」符合「<EM>first</EM> tes」。於是引擎將「>」與「t」進行符合。顯然還是會失敗。這個過程繼續,直到「<.+」符合「<EM>first</EM」,「>」與「>」符合。於是引擎找到了一個符合「<EM>first</EM>」。記住,正則導向的引擎是「急切的」,所以它會急著報告它找到的第一個符合。而不是繼續回溯,即使可能會有更好的符合,例如「<EM>」。所以我們可以看到,由於「+」的貪婪性,使得正則表達式引擎返回了一個最左邊的最長的符合。
用懶惰性取代貪婪性
一個用於修正以上問題的可能專案是用「+」的惰性代替貪婪性。你可以在「+」後面緊跟一個問號「?」來達到這一點。「*」,「{}」和「?」表示的重複也可以用這個專案。因此在上面的例子中我們可以使用「<.+?>」。讓我們再來看看正則表達式引擎的處理過程。
再一次,正則表達式記號「<」會符合字串的第一個「<」。下一個正則記號是「.」。這次是一個懶惰的「+」來重複上一個字元。這告訴正則引擎,盡可能少的重複上一個字元。因此引擎符合「.」和字元「E」,然後用「>」符合「M」,結果失敗了。引擎會進行回溯,和上一個例子不同,因為是惰性重複,所以引擎是延伸惰性重複而不是減少,於是「<.+」現在被延伸為「<EM」。引擎繼續符合下一個記號「>」。這次得到了一個完成符合。引擎於是報告「<EM>」是一個完成的符合。整個過程大致如此。
惰性延伸的一個替代專案
我們還有一個更好的替代專案。可以用一個貪婪重複與一個取反字集:「<[^>]+>」。之所以說這是一個更好的專案在於使用惰性重複時,引擎會在找到一個完成符合前對每一個字元進行回溯。而使用取反字集則不需要進行回溯。
最後要記住的是,本教學僅僅談到的是正則導向的引擎。文字導向的引擎是不回溯的。但是同時他們也不支援惰性重複操作。
7.使用「.」符合幾乎任意字元
在正則表達式中,「.」是最常用的符號之一。不幸的是,它也是最容易被誤用的符號之一。
「.」符合一個單個的字元而不用關心被符合的字元是什麼。唯一的例外是新行符。在本教學中談到的引擎,預設情況下都是不符合新行符的。因此在預設情況下,「.」等於是字集[^
](Window)或[^
]( Unix)的簡寫。
這個例外是因為歷史的原因。因為早期使用正則表達式的工具是基於行的。它們都是一行一行的讀入一個檔案,將正則表達式分別應用到每一行上去。在這些工具中,字串是不包括新行符的。因此「.」也就永遠不符合新行符。
現代的工具和語系能夠將正則表達式應用到很大的字串甚至整個檔案上去。本教學討論的所有正則表達式實現都提供一個選項,可以使「.」符合所有的字元,內含新行符。在RegexBuddy, EditPad Pro或PowerGREP等工具中,你可以簡單的選中「點號符合新行符」。在Perl中,「.」可以符合新行符的模式被稱作「單行模式」。很不幸,這是一個很容易混淆的名詞。因為還有所謂「多行模式」。多行模式只影響行首行尾的錨定(anchor),而單行模式只影響「.」。
其他語系和正則表達式庫也採用了Perl的術語定義。當在.NET Framework中使用正則表達式類時,你可以用類似下面的語句來啟動單行模式:Regex.Match(「string」,」regex」,RegexOptions.SingleLine)
保守的使用點號「.」
點號可以說是最強大的元字元。它容許你偷懶:用一個點號,就能符合幾乎所有的字元。但是問題在於,它也常常會符合不該符合的字元。
我會以一個簡單的例子來說明。讓我們看看如何符合一個具有「mm/dd/yy」格式的日期,但是我們想容許使用者來選取分隔設定。很快能想到的一個專案是<<dd.dd.dd>>。看上去它能符合日期「02/12/03」。問題在於02512703也會被認為是一個有效的日期。
<<dd[-/.]dd[-/.]dd>>看上去是一個好一點的解決專案。記住點號在一個字集裡不是元字元。這個專案遠不夠完善,它會符合「99/99/99」。而<<[0-1]d[-/.][0-3]d[-/.]dd>>又更進一步。儘管他也會符合「19/39/99」。你想要你的正則表達式達到如何完美的程度取決於你想達到什麼樣的目的。如果你想校驗使用者輸入,則需要盡可能的完美。如果你只是想分析一個已知的源,並且我們知道沒有錯誤的資料,用一個比較好的正則表達式來符合你想要搜尋的字元就已經足夠。
8.字串開始和結束的錨定
錨定和一般的正則表達式符號不同,它不符合任何字元。相反,他們符合的是字元之前或之後的位置。「^」符合一行字串第一個字元前的位置。<<^a>>將會符合字串「abc」中的a。<<^b>>將不會符合「abc」中的任何字元。
類似的,$符合字串中最後一個字元的後面的位置。所以<<c$>>符合「abc」中的c。
錨定的應用
在寫程式語系中校驗使用者輸入時,使用錨定是非常重要的。如果你想校驗使用者的輸入為整數,用<<^d+$>>。
使用者輸入中,常常會有多餘的前導空格或結束空格。你可以用<<^s*>>和<<s*$>>來符合前導空格或結束空格。
使用「^」和「$」作為行的開始和結束錨定
如果你有一個包括了多行的字串。例如:「first line
second line」(其中
表示一個新行符)。常常需要對每行分別處理而不是整個字串。因此,幾乎所有的正則表達式引擎都提供一個選項,可以延伸這兩種錨定的含義。「^」可以符合字串的開始位置(在f之前),以及每一個新行符的後面位置(在
和s之間)。類似的,$會符合字串的結束位置(最後一個e之後),以及每個新行符的前面(在e與
之間)。
在.NET中,當你使用如下代碼時,將會定義錨定符合每一個新行符的前面和後面位置:Regex.Match(「string」, 「regex」, RegexOptions.Multiline)
應用:string str = Regex.Replace(Original, 「^」, 「> 「, RegexOptions.Multiline)–將會在每行的行首插入「> 」。
絕對錨定
<<A>>只符合整個字串的開始位置,<<>>只符合整個字串的結束位置。即使你使用了「多行模式」,<<A>>和<<>>也永遠不符合新行符。
即使和$只符合字串的結束位置,仍然有一個例外的情況。如果字串以新行符結束,則和$將會符合新行符前面的位置,而不是整個字串的最後面。這個「改進」是由Perl引進的,然後被許多的正則表達式實現所遵循,內含Java,.NET等。如果應用<<^[a-z]+$>>到「joe
」,則符合結果是「joe」而不是「joe
」。
9.單詞邊界
元字元<<>>也是一種對位置進行符合的「錨」。這種符合是0長度符合。
有4種位置被認為是「單詞邊界」:
1) 在字串的第一個字元前的位置(如果字串的第一個字元是一個「單詞字元」)
2) 在字串的最後一個字元後的位置(如果字串的最後一個字元是一個「單詞字元」)
3) 在一個「單詞字元」和「非單詞字元」之間,其中「非單詞字元」緊跟在「單詞字元」之後
4) 在一個「非單詞字元」和「單詞字元」之間,其中「單詞字元」緊跟在「非單詞字元」後面
「單詞字元」是可以用「w」符合的字元,「非單詞字元」是可以用「W」符合的字元。在大多數的正則表達式實現中,「單詞字元」通常內含<<[a-zA-Z0-9_]>>。
例如:<<4>>能夠符合單個的4而不是一個更大數的一部分。這個正則表達式不會符合「44」中的4。
換種說法,幾乎可以說<<>>符合一個「字母數字序列」的開始和結束的位置。
「單詞邊界」的取反集為<<B>>,他要符合的位置是兩個「單詞字元」之間或是兩個「非單詞字元」之間的位置。
深入正則表達式引擎內定
讓我們看看把正則表達式<<is>>應用到字串「This island is beautiful」。引擎先處理符號<<>>。因為是0長度 ,所以第一個字元T前面的位置會被考察。因為T是一個「單詞字元」,而它前面的字元是一個空字元(void),所以符合了單詞邊界。接著<<i>>和第一個字元「T」符合失敗。符合過程繼續進行,直到第五個空格符,和第四個字元「s」之間又符合了<<>>。然而空格符和<<i>>不符合。繼續向後,到了第六個字元「i」,和第五個空格字元之間符合了<<>>,然後<<is>>和第六、第七個字元都符合了。然而第八個字元和第二個「單詞邊界」不符合,所以符合又失敗了。到了第13個字元i,因為和前面一個空格符形成「單詞邊界」,同時<<is>>和「is」符合。引擎接著嘗試符合第二個<<>>。因為第15個空格符和「s」形成單詞邊界,所以符合完成。引擎「急著」返回完成符合的結果。
10. 選取符
正則表達式中「|」表示選取。你可以用選取符符合多個可能的正則表達式中的一個。
如果你想搜尋文字「cat」或「dog」,你可以用<<cat|dog>>。如果你想有更多的選取,你只要延伸清單<<cat|dog|mouse|fish>>。
選取符在正則表達式中具有最低的優先級,也就是說,它告訴引擎要麼符合選取符左邊的所有表達式,要麼符合右邊的所有表達式。你也可以用圓括號來限制選取符的作用範圍。如<<(cat|dog)>>,這樣告訴正則引擎把(cat|dog)當成一個正則表達式單位來處理。
注意正則引擎的「急於表功」性
正則引擎是急切的,當它找到一個有效的符合時,它會停止搜尋。因此在一定條件下,選取符兩邊的表達式的順序對結果會有影響。假設你想用正則表達式搜尋一個寫程式語系的函數清單:Get,GetValue,Set或SetValue。一個明顯的解決專案是<<Get|GetValue|Set|SetValue>>。讓我們看看當搜尋SetValue時的結果。
因為<<Get>>和<<GetValue>>都失敗了,而<<Set>>符合完成。因為正則導向的引擎都是「急切」的,所以它會返回第一個完成的符合,就是「Set」,而不去繼續搜尋是否有其他更好的符合。
和我們期望的相反,正則表達式並沒有符合整個字串。有幾種可能的解決辦法。一是考慮到正則引擎的「急切」性,改變選項的順序,例如我們使用<<GetValue|Get|SetValue|Set>>,這樣我們就可以優先搜尋最長的符合。我們也可以把四個選項結合起來成兩個選項:<<Get(Value)?|Set(Value)?>>。因為問號重複符是貪婪的,所以SetValue總會在Set之前被符合。
一個更好的專案是使用單詞邊界:<<(Get|GetValue|Set|SetValue)>>或<<(Get(Value)?|Set(Value)?>>。更進一步,既然所有的選取都有相同的結尾,我們可以把正則表達式改善為<<(Get|Set)(Value)?>>。
11. 組與向後引用
把正則表達式的一部分放在圓括號內,你可以將它們形成組。然後你可以對整個組使用一些正則操作,例如重複操作符。
要注意的是,只有圓括號「()」才能用於形成組。「[]」用於定義字集。「{}」用於定義重複操作。
當用「()」定義了一個正則表達式組後,正則引擎則會把被符合的組按照順序編號,存入快取。當對被符合的組進行向後引用的時候,可以用「數字」的模式進行引用。<<1>>引用第一個符合的後向引用組,<<2>>引用第二個組,以此類推,<<
>>引用第n個組。而<< >>則引用整個被符合的正則表達式本身。我們看一個例子。
假設你想符合一個HTML標籤的開始標籤和結束標籤,以及標籤中間的文字。比如<B>This is a test</B>,我們要符合<B>和</B>以及中間的文字。我們可以用如下正則表達式:「<([A-Z][A-Z0-9]*)[^>]*>.*?</1>」
首先,「<」將會符合「<B>」的第一個字元「<」。然後[A-Z]符合B,[A-Z0-9]*將會符合0到多次字母數字,後面緊接著0到多個非「>」的字元。最後正則表達式的「>」將會符合「<B>」的「>」。接下來正則引擎將對結束標籤之前的字元進行惰性符合,直到遇到一個「</」符號。然後正則表達式中的「1」表示對前面符合的組「([A-Z][A-Z0-9]*)」進行引用,在本例中,被引用的是標籤名「B」。所以需要被符合的結尾標籤為「</B>」
你可以對相同的後向引用組進行多次引用,<<([a-c])x1x1>>將符合「axaxa」、「bxbxb」以及「cxcxc」。如果用數字形式引用的組沒有有效的符合,則引用到的內容簡單的為空。
一個後向引用不能用於它自身。<<([abc]1)>>是錯誤的。因此你不能將<< >>用於一個正則表達式符合本身,它只能用於置換操作中。
後向引用不能用於字集內定。<<(a)[1b]>>中的<<1>>並不表示後向引用。在字集內定,<<1>>可以被解釋為八進位形式的轉碼。
向後引用會降低引擎的速度,因為它需要存儲符合的組。如果你不需要向後引用,你可以告訴引擎對某個組不存儲。例如:<<Get(?:Value)>>。其中「(」後面緊跟的「?:」會告訴引擎對於組(Value),不存儲符合的值以供後向引用。
重複操作與後向引用
當對組使用重複操作符時,快取裡後向引用內容會被不斷重整,只保留最後符合的內容。例如:<<([abc]+)=1>>將符合「cab=cab」,但是<<([abc])+=1>>卻不會。因為([abc])第一次符合「c」時,「1」代表「c」;然後([abc])會繼續符合「a」和「b」。最後「1」代表「b」,所以它會符合「cab=b」。
應用:檢查重複單詞–當編輯文字時,很容易就會輸入重複單詞,例如「the the」。使用<<(w+)s+1>>可以檢驗到這些重複單詞。要刪除第二個單詞,只要簡單的利用置換功能置換掉「1」就可以了。
組的命名和引用
在PHP,Python中,可以用<<(?P<name>group)>>來對組進行命名。在本例中,詞法?P<name>就是對組(group)進行了命名。其中name是你對組的起的名字。你可以用(?P=name)進行引用。
.NET的命名組
.NET framework也支援命名組。不幸的是,微軟的程式員們決定發明他們自己的語法,而不是沿用Perl、Python的規則。目前為止,還沒有任何其他的正則表達式實現支援微軟發明的語法。
下面是.NET中的例子:
(?<first>group)(?』second』group)
正如你所看到的,.NET提供兩種詞法來建立命名組:一是用尖括號「<>」,或是用單引號「』』」。尖括號在字串中使用更方便,單引號在ASP代碼中更有用,因為ASP代碼中「<>」被用作HTML標籤。
要引用一個命名組,使用k<name>或k』name』.
當進行搜尋置換時,你可以用「${name}」來引用一個命名組。
12. 正則表達式的符合模式
本教學所討論的正則表達式引擎都支援三種符合模式:
<</i>>使正則表達式對大小寫不敏感,
<</s>>開啟「單行模式」,即點號「.」符合新行符
<</m>>開啟「多行模式」,即「^」和「$」符合新行符的前面和後面的位置。
在正則表達式內定開啟或關閉模式
如果你在正則表達式內定插入修飾符(?ism),則該修飾符只對其右邊的正則表達式起作用。(?-i)是關閉大小寫不敏感。你可以很快的進行測試。<<(?i)te(?-i)st>>應該符合TEst,但是不能符合teST或TEST.
13. 原子組與防止回溯
在一些特殊情況下,因為回溯會使得引擎的效率極其低下。
讓我們看一個例子:要符合這樣的字串,字串中的每個欄位間用逗號做分隔設定,第12個欄位由P開頭。
我們容易想到這樣的正則表達式<<^(.*?,){11}P>>。這個正則表達式在標準情況下工作的很好。但是在極端情況下,如果第12個欄位不是由P開頭,則會發生災難性的回溯。如要搜尋的字串為「1,2,3,4,5,6,7,8,9,10,11,12,13」。首先,正則表達式一直完成符合直到第12個字元。這時,前面的正則表達式消耗的字串為「1,2,3,4,5,6,7,8,9,10,11,」,到了下一個字元,<<P>>並不符合「12」。所以引擎進行回溯,這時正則表達式消耗的字串為「1,2,3,4,5,6,7,8,9,10,11」。繼續下一次符合過程,下一個正則符號為點號<<.>>,可以符合下一個逗號「,」。然而<<,>>並不符合字元「12」中的「1」。符合失敗,繼續回溯。大家可以想像,這樣的回溯組合是個非常大的數量。因此可能會造成引擎崩潰。
用於阻止這樣巨大的回溯有幾種專案:
一種簡單的專案是盡可能的使符合精確。用取反字集代替點號。例如我們用如下正則表達式<<^([^,
]*,){11}P>>,這樣可以使失敗回溯的次數下降到11次。
另一種專案是使用原子組。
原子組的目的是使正則引擎失敗的更快一點。因此可以有效的阻止海量回溯。原子組的語法是<<(?>正則表達式)>>。位於(?>)之間的所有正則表達式都會被認為是一個單一的正則符號。一旦符合失敗,引擎將會回溯到原子組前面的正則表達式部分。前面的例子用原子組可以表達成<<^(?>(.*?,){11})P>>。一旦第十二個欄位符合失敗,引擎回溯到原子組前面的<<^>>。
14. 向前檢視與向後檢視
Perl 5 引入了兩個強大的正則語法:「向前檢視」和「向後檢視」。他們也被稱作「零長度斷言」。他們和錨定一樣都是零長度的(所謂零長度即指該正則表達式不消耗被符合的字串)。不同之處在於「前後檢視」會實際符合字元,只是他們會拋棄符合只返回符合結果:符合或不符合。這就是為什麼他們被稱作「斷言」。他們並不實際消耗字串中的字元,而只是斷言一個符合是否可能。
幾乎本文討論的所有正則表達式的實現都支援「向前嚮後檢視」。唯一的一個例外是Javascript只支援向前檢視。
肯定和否定式的向前檢視
如我們前面提過的一個例子:要尋找一個q,後面沒有緊跟一個u。也就是說,要麼q後面沒有字元,要麼後面的字元不是u。採用否定式向前檢視後的一個解決專案為<<q(?!u)>>。否定式向前檢視的語法是<<(?!檢視的內容)>>。
肯定式向前檢視和否定式向前檢視很類似:<<(?=檢視的內容)>>。
如果在「檢視的內容」部分有組,也會產生一個向後引用。但是向前檢視本身並不會產生向後引用,也不會被計入向後引用的編號中。這是因為向前檢視本身是會被拋棄掉的,只保留符合與否的判斷結果。如果你想保留符合的結果作為向後引用,你可以用<<(?=(regex))>>來產生一個向後引用。
肯定和否定式的先後檢視
向後檢視和向前檢視有相同的效果,只是方向相反
否定式向後檢視的語法是:<<(?<!檢視內容)>>
肯定式向後檢視的語法是:<<(?<=檢視內容)>>
我們可以看到,和向前檢視相比,多了一個表示方向的左尖括號。
例:<<(?<!a)b>>將會符合一個沒有「a」作前導字元的「b」。
值得注意的是:向前檢視從現用的字串位置開始對「檢視」正則表達式進行符合;向後檢視則從現用的字串位置開始先後回溯一個字元,然後再開始對「檢視」正則表達式進行符合。
深入正則表達式引擎內定
讓我們看一個簡單例子。
把正則表達式<<q(?!u)>>應用到字串「Iraq」。正則表達式的第一個符號是<<q>>。正如我們知道的,引擎在符合<<q>>以前會掃過整個字串。當第四個字元「q」被符合後,「q」後面是空字元(void)。而下一個正則符號是向前檢視。引擎注意到已經進入了一個向前檢視正則表達式部分。下一個正則符號是<<u>>,和空字元不符合,從而導致向前檢視裡的正則表達式符合失敗。因為是一個否定式的向前檢視,意味著整個向前檢視結果是完成的。於是符合結果「q」被返回了。
我們在把相同的正則表達式應用到「quit」。<<q>>符合了「q」。下一個正則符號是向前檢視部分的<<u>>,它符合了字串中的第二個字元「i」。引擎繼續走到下個字元「i」。然而引擎這時注意到向前檢視部分已經處理完了,並且向前檢視已經完成。於是引擎拋棄被符合的字串部分,這將導致引擎回退到字元「u」。
因為向前檢視是否定式的,意味著檢視部分的完成符合導致了整個向前檢視的失敗,因此引擎不得不進行回溯。最後因為再沒有其他的「q」和<<q>>符合,所以整個符合失敗了。
為了確保你能清楚地理解向前檢視的實現,讓我們把<<q(?=u)i>>應用到「quit」。<<q>>首先符合「q」。然後向前檢視完成符合「u」,符合的部分被拋棄,只返回可以符合的判斷結果。引擎從字元「i」回退到「u」。由於向前檢視完成了,引擎繼續處理下一個正則符號<<i>>。結果發現<<i>>和「u」不符合。因此符合失敗了。由於後面沒有其他的「q」,整個正則表達式的符合失敗了。
更進一步理解正則表達式引擎內定機制
讓我們把<<(?<=a)b>>應用到「thingamabob」。引擎開始處理向後檢視部分的正則符號和字串中的第一個字元。在這個例子中,向後檢視告訴正則表達式引擎回退一個字元,然後檢視是否有一個「a」被符合。因為在「t」前面沒有字元,所以引擎不能回退。因此向後檢視失敗了。引擎繼續走到下一個字元「h」。再一次,引擎暫時回退一個字元並檢查是否有個「a」被符合。結果發現了一個「t」。向後檢視又失敗了。
向後檢視繼續失敗,直到正則表達式到達了字串中的「m」,於是肯定式的向後檢視被符合了。因為它是零長度的,字串的現用的位置仍然是「m」。下一個正則符號是<<b>>,和「m」符合失敗。下一個字元是字串中的第二個「a」。引擎向後暫時回退一個字元,並且發現<<a>>不符合「m」。
在下一個字元是字串中的第一個「b」。引擎暫時性的向後退一個字元發現向後檢視被滿足了,同時<<b>>符合了「b」。因此整個正則表達式被符合了。作為結果,正則表達式返回字串中的第一個「b」。
向前嚮後檢視的應用
我們來看這樣一個例子:尋找一個具有6位字元的,含有「cat」的單詞。
首先,我們可以不用向前嚮後檢視來解決問題,例如:
<< catw{3}|wcatw{2}|w{2}catw|w{3}cat>>
足夠簡單吧!但是當需求變成尋找一個具有6-12位字元,含有「cat」,「dog」或「mouse」的單詞時,這種方法就變得有些笨拙了。
我們來看看使用向前檢視的專案。在這個例子中,我們有兩個基本需求要滿足:一是我們需要一個6位的字元,二是單詞含有「cat」。
滿足第一個需求的正則表達式為<<w{6}>>。滿足第二個需求的正則表達式為<<w*catw*>>。
把兩者結合起來,我們可以得到如下的正則表達式:
<<(?=w{6})w*catw*>>
具體的符合過程留給讀者。但是要注意的一點是,向前檢視是不消耗字元的,因此當判斷單詞滿足具有6個字元的條件後,引擎會從開始判斷前的位置繼續對後面的正則表達式進行符合。
最後作些改善,可以得到下面的正則表達式:
<<(?=w{6})w{0,3}catw*>>
15. 正則表達式中的條件測試
條件測試的語法為<<(?ifthen|else)>>。「if」部分可以是向前嚮後檢視表達式。如果用向前檢視,則語法變為:<<(?(?=regex)then|else)>>,其中else部
分是可選的。
如果if部分為true,則正則引擎會試圖符合then部分,否則引擎會試圖符合else部分。
需要記住的是,向前先後檢視並不實際消耗任何字元,因此後面的then與else部分的符合時從if測試前的部分開始進行嘗試。
16. 為正則表達式加入註釋
在正則表達式中加入註釋的語法是:<<(?#comment)>>
例:為用於符合有效日期的正則表達式加入註釋:
(?#year)(19|20)dd[- /.](?#month)(0[1-9]|1[012])[- /.](?#day)(0[1-9]|[12][0-9]|3[01])
原文出處:http://imsiren.com/archives/560
PHP程式設計問題諮詢:
請什麼是UBB代碼?使用UBB代碼有何好處?
PHP程式設計問題回覆:
UBB代碼,可以參考以下的說明:
UBB代碼是HTML的一個變種,是Ultimate Bulletin Board (國外一個BBS程式,國內也有不少地方使用這個程式)採用的一種特殊的TAG.
即使禁止使用 HTML,你也可以用 UBBCode? 來實現.也許你更希望使用 UBBCode? 而不是 HTML, 即使論壇允許使用 HTML, 因為使用起來代碼較少也更安全.
PHP攫取代碼案例,支援正則表達式設定從開始到結束區功能變數,代碼如下:
<?php
function preg_substr($start, $end, $str) // 正則截取函數
{
$temp = preg_split($start, $str);
$content = preg_split($end, $temp[1]);
return $content[0];
}
function str_substr($start, $end, $str) // 字串截取函數
{
$temp = explode($start, $str, 2);
$content = explode($end, $temp[1], 2);
return $content[0];
}
// —————- 使用案例 —————-
$str = iconv("UTF-8", "big5", file_get_contents("http://www.mycodes.net"));
echo ('標題: ' . str_substr("<title>", "</title>", $str)); // 通過字串提取標題
echo ('作者: ' . preg_substr("/userid=d+">/", "/<//", $str)); // 通過正則提取作者
echo ('內容: ' . str_substr('<div class="content">', '</div>', $str)); //內容當然不可以少
?>
FPDF的功能確實很強大,但它是如何實現PDF檔案生成的呢,我決定深入分析一下它的代碼,以求對其進行改進,應用到自己的項目中。
FPDF實際上是一個類,由於該類在php4和5下都能標準運行,那麼就從PHP4的角度來分析它。
首先從類的屬性、構造函數開始,然後按類的公共方法的順序進行分析,遇到呼叫其他方法的時候將其提前進行分析。
首先來看類的屬性:
var $page; // 現用的頁數
var $n; // 現用的對像數
var $offsets; //array of object offsets
var $buffer; // PDF 快取
var $pages; // 包括頁數的陣列
var $state; // 現用的文件狀態
var $compress; // 壓縮標示
var $k; // 比例因子(使用者指定單位內的點)
var $DefOrientation; // 預設方向
var $CurOrientation; // 現用的方向
var $PageFormats; // 可用的頁格式
var $DefPageFormat; // 預設的頁格式
var $CurPageFormat; // 現用的的頁格式
var $PageSizes; // 儲存非預設頁面大小的陣列
var $wPt,$hPt; // 用點表示的現用的頁尺寸
var $w,$h; // 使用者指定單位表示的現用的頁尺寸
var $lMargin; // 頁面左邊距
var $tMargin; // 頁面上邊距
var $rMargin; // 頁面右邊距
var $bMargin; // 換版邊
var $cMargin; // 內定單元邊距
var $x,$y; // 使用者單位表示的現用的坐標
var $lasth; // 最近輸出單元的高度
var $LineWidth; // 使用者單位表示的線寬,即粗細
var $CoreFonts; // 標準字型名稱的陣列
var $fonts; // 用到的字型的陣列
var $FontFiles; // 字型檔案的陣列
var $diffs; // 編碼差異的陣列
var $FontFamily; // 現用的字型
var $FontStyle; // 現用的字型樣式
var $underline; // 下劃線標示
var $CurrentFont; // 現用的字型訊息
var $FontSizePt; // 用點表示現用的字型大小
var $FontSize; // 使用者單位表示現用的字型大小
var $DrawColor; // 繪圖彩色指令
var $FillColor; // 填充彩色指令
var $TextColor; // 文字彩色指令
var $ColorFlag; // 指出填充彩色和文字彩色是否不同
var $ws; // 文字間距
var $images; // 用到的圖片的陣列
var $PageLinks; // 頁面連結的陣列
var $links; // 內定連結的陣列
var $AutoPageBreak; // 自動分頁
var $PageBreakTrigger; // 自動分頁的觸發值,即最大內容高度
var $InHeader; // 處理頁眉時的標示
var $InFooter; // 處理頁腳時的標示
var $ZoomMode; // 顯示時的縮放模式
var $LayoutMode; // 分頁顯示模式
var $title; // 檔案標題
var $subject; // 檔案主旨
var $author; // 檔案作者
var $keywords; // 檔案關鍵詞
var $creator; // 應用程式,不知道啥意思,PDF屬性裡的
var $AliasNbPages; // 頁面總數的別名,用於在該位置顯示頁面總數
var $PDFVersion; // PDF 版本號
這裡把註釋換成了中文的,僅供參考吧!
在網站PHP程式網頁中,如何生成靜態頁面的方法?
在PHP網站開發中為了網站推廣和SEO等需要,需要對網站進行全站或局部靜態化處理,PHP生成靜態HTML頁面有多種方法,比如利用PHP範本、快取等實現頁面靜態化,今天就以PHP案例教學形式討論PHP生成靜態頁面的方法。頁面靜態化的方法,分為兩種,一種是偽靜態,就是url 重寫,一種是你真的靜態化。下面介紹PHP中頁面靜態化的方法。
什麼是PHP靜態化
PHP靜態化的簡單理解就是使網站生成頁面以靜態HTML的形式展現在訪客面前,PHP靜態化分純靜態化和偽靜態化,兩者的區別在於PHP生成靜態頁面的處理機制不同。
PHP生成靜態HTML頁面的方法
1、利用PHP範本生成靜態頁面
PHP範本實現靜態化非常方便,比如安裝和使用PHP Smarty實現網站靜態化。
2、使用PHP檔案讀寫功能生成靜態頁面
PHP生成靜態頁面案例代碼
<?
$out1 = "<html><head><title>PHP網站靜態化教學</title></head>
<body>歡迎訪問PHP網站開發教學網http://por.tw,本文主要介紹PHP網站頁面靜態化的方法
</body></html>";
$fp = fopen("leapsoulcn.html","w");
if(!$fp)
{
echo "System Error";
exit();
}
else {
fwrite($fp,$out1);
fclose($fp);
echo "Success";
}
?>
3、使用PHP輸出控制函數(Output Control)生成靜態頁面
輸出控制函數(Output Control)也就是使用和控制快取來生成靜態HTML頁面,也會使用到PHP檔案讀寫函數。
PHP生成靜態頁面案例代碼
<?
ob_start();
echo "<html>".
"<head>".
"<title>PHP網站靜態化教學</title>".
"</head>".
"<body>歡迎訪問PHP網站開發教學網http://por.tw,本文主要介紹PHP網站頁面靜態化的方法</body>".
"</html>";
$out1 = ob_get_contents();
ob_end_clean();
$fp = fopen("leapsoulcn.html","w");
if(!$fp)
{
echo "System Error";
exit();
}
else
{
fwrite($fp,$out1);
fclose($fp);
echo "Success";
}
?>
我們知道使用PHP進行網站開發,一般執行結果直接輸出到遊覽器,為了使用PHP生成靜態頁面,就需要使用輸出控制函數控制快取區,以便取得快取區的內容,然後再輸出到靜態HTML頁面檔案中以實現網站靜態化。
PHP生成靜態頁面的思路為:首先開啟快取,然後輸出了HTML內容(你也可以通過include將HTML內容以檔案形式包括進來),之後取得快取中的內容,清理快取後通過PHP檔案讀寫函數將快取內容寫入到靜態HTML頁面檔案中。PHP檔案讀寫教學?
獲得輸出的快取內容以生成靜態HTML頁面的過程需要使用三個函數:ob_start()、ob_get_contents()、ob_end_clean()。
網站PHP程式網頁生成靜態頁面的方法知識點:
1、ob_start函數一般主要是用來開啟快取,注意使用ob_start之前不能有任何輸出,如空格、字元等。
2、ob_get_contents函數主要用來取得快取中的內容以字串形式返回,注意此函數必須在ob_end_clean函數之前呼叫,否則取得不到快取內容。
3、ob_end_clean函數主要是清理快取中的內容並關閉快取,完成則返回True,失敗則返回False
PHP輸出控制函數(Output Control)有很多應用,今後將陸續展開。
至此,使用PHP生成靜態HTML頁面以實現網站靜態化的方法就介紹完了,根據實際情況和需求你可以選取不同的靜態化方法。
用php生成UTF-8的檔案沒有什麼特別的,只要把我們執行的那個PHP檔案改成UTF-8編碼儲存起來,然後頁面內容也用UTF-8的編碼就可以了。
代碼如下:
header('Content-Type: text/html; charset=utf-8');
$text=」荊門線上 0724.CC 普通文字帶漢字生成」;
$f=fopen("0724.php", 「wb」);
fwrite($f, $text);
fclose($f);
?>
這樣雖然上面定義了utf-8生成的是 utf-8 的格式,我們來簡單的改下就可以了 因為這樣儲存的檔案在記事中檢視並不是utf-8的編碼,所以我們看下面代碼
$xml = '網路線上 普通文字帶中文字生成';
$h = fopen (0725.php,'w');
if ($h) {
fwrite ($h,"xEFxBBxBF".iconv('utf-8','utf-8',$xml));
}
fclose ($h);
前面這個 xEFxBBxBF 一定不能忘記呀
OK,這時候生成的檔案就是我們要的 utf-8格式的編碼檔案。
網站發生警語:atal error: Allowed memory size of 16777216 bytes exhausted(記憶體不夠用)之處理方法
網站架站或網站升級後,網站發生類似警語:atal error: Allowed memory size of XXXXXXXX bytes exhausted
不管是管理介面(Dashboard),或者登入出問題,看來幾乎都是記憶體不夠用。
例如其發生的警語:
Fatal error: Allowed memory size of 16777216 bytes exhausted (tried to allocate 4023 bytes)
這是什麼意思呢?又該如何解決?
答案是:記憶體的需求是更大了,這邊提供幾個解決的方法。
當出現以下的類似情況,要來調整PHP的記憶體,來執行更多的程式
1.找到php.ini的路徑(/etc/php5/apache2/php.ini)
2.搜尋:memory_limit 字串
3.找到:memory_limit = 16M ; Maximum amount of memory a script may consume (16MB)
A.如果有權限修改php.ini,那麼請將memory_limit = 32M的32提高,看是要64還是96或者128。
B.如果沒有權限修改php.ini,可以在.htaccess裡面加上php_value memory_limit 32M,或者更高的數字。
備註:php.ini修改後apache要重新啟動才會生效!
語法如下:
service apache2 restart
————————————————————-
如何使用PHP執行linux系統指令呢?
相信這是很多學員想知道答案!
首先先要給大家介紹PHP執行linux系統指令的幾個基本函數。
system函數
說明:執行外部程式並顯示輸出資料。
語法:string system(string command, int [return_var]);
返回值: 字串
詳細介紹:
本函數就像是 C 語中的函數 system(),用來執行指令,並輸出結果。若是 return_var 參數存在,則執行 command 之後的狀態會填入 return_var 中。同樣值得注意的是若需要處理使用者輸入的資料,而又要防止使用者耍花招破解系統,則可以使用 EscapeShellCmd()。若 PHP 以模組式的執行,本函數會在每一行輸出後自動更新 Web 伺服器的輸出緩衝暫存區。若需要完整的返回字串,且不想經由不必要的其它中間的輸出介面,可以使用 PassThru()。
案例代碼:
< ?php
$last_line = system('ls', $retval);
echo 'Last line of the output: ' . $last_line;
echo '<hr />Return value: ' . $retval;
?>
exec函數
說明:執行外部程式。
語法:string exec(string command, string [array], int [return_var]);
返回值: 字串
詳細介紹:
本函數執行輸入 command 的外部程式或外部指令。
它的返回字串只是外部程式執行後返回的最後一行;若需要完整的返回字串,可以使用 PassThru() 這個函數。
要是參數 array 存在,command 會將 array 加到參數中執行,若不欲 array 被處理,可以在執行 exec() 之前呼叫 unset()。
若是 return_var 跟 array 二個參數都存在,則執行 command 之後的狀態會填入 return_var 中。
值得注意的是若需要處理使用者輸入的資料,而又要防止使用者耍花招破解系統,則可以使用 EscapeShellCmd()。
案例代碼:
<?php
echo exec('whoami');
?>
popen函數
說明:開啟檔案。
語法:int popen(string command, string mode);
返回值: 整數
詳細介紹:
本函數執行指令開檔,而該檔案是用管道模式處理的檔案。
用本函數開啟的檔案只能是單向的 (只能讀或只能寫),而且一定要用 pclose() 關閉。
在檔案操作上可使用 fgets()、fgetss() 與 fputs()。若是開檔發生錯誤,返回 false 值。
案例代碼:
<?
$fp = popen( "/bin/ls", "r" );
?>
資料加密在我們生活中的地位已經越來越重要了,尤其是考慮到在網路上發生的大量交易和傳輸的大量資料。
對於不需要復原為原始資料的訊息我們可以使用MD5、sha1等不可逆加密算法對資料進行加密處理,但對於交易訊息等需要復原為原始資料的重要訊息則必須使用可復原的加密算法進行加密了。
當然你可以自己寫一個可逆加密的算法進行加密和解密計算。
本文中我們介紹的是使用 mcrypt模組進行加密解密操作。
Mcrypt的優點不僅僅在於其提供的加密算法較多,在windows下隨PHP包一起發佈,還在於它可以對資料進行加/解密處理,此外,它還提供了內含DES算法在內的35種處理資料用的函數。
/**
+—————————————————–
* Mcrypt 加密/解密
* @param String $date 要加密和解密的資料
* @param String $mode encode 預設為加密/decode 為解密
* @return String
+—————————————————–
* @example
*/
function ZxingCrypt($date,$mode = 'encode'){
$key = md5('zxing');//用MD5哈希生成一個密鑰,注意加密和解密的密鑰必須統一
if ($mode == 'decode'){
$date = base64_decode($date);
}
if (function_exists('mcrypt_create_iv')){
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
}
if (isset($iv) && $mode == 'encode'){
$passcrypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $date, MCRYPT_MODE_ECB, $iv);
}elseif (isset($iv) && $mode == 'decode'){
$passcrypt = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $date, MCRYPT_MODE_ECB, $iv);
}
if ($mode == 'encode'){
$passcrypt = base64_encode($passcrypt);
}
return $passcrypt;
}
—————————————————————-
隱藏apache和php的版本訊息可以盡量減少敏感訊息洩露的方法,並不會使你的伺服器真的更安全,如果你現在使用的版本比較舊,請務必盡快將系統升級,降低被蠕蟲攻擊的風險。
隱藏apache和php的版本訊息,web server避免一些不必要的麻煩,可以把apache和php的版本訊息不顯示
隱藏 Apache 版本訊息
/etc/apache2/apache2.conf 或 /etc/httpd/conf/httpd.conf
ServerTokens ProductOnly
ServerSignature Off
重啟 apache
現在 http 頭裡面只看到:
Server: Apache
隱藏 PHP 版本
php.ini
expose_php On
改成
expose_php Off
重啟apache後,php版本在http頭中隱藏了。
詳解 :
為了防止某些別有用心的傢伙窺視我們的伺服器,應該做些什麼.
我們來看一下關聯的2個參數,分別為ServerTokens和ServerSignature,通過控制這2個閥門應該就能起到一些作用,比如我們可以在配置檔案中這麼寫:
ServerTokens Prod
ServerSignature Off
ServerTokens
用於控制伺服器是否相應來自使用者端的請求,向使用者端輸出伺服器系統類型或內建模組等重要的系統訊息。 在主配置檔案中提供全局控制預設閥值為”Full”(ServerTokens Full),所以,如果你的Linux發行版本沒有變更過這個閥值的話,所有與你的系統有關的敏感訊息都會向全世界公開。比如RHEL會將該 閥值變更為”ServerTokens OS”,而Debian依然使用預設的”Full”閥值
以apache-2.0.55為例,閥值可以設定為以下某項(後面為相對應的Banner Header):
Prod >>> Server: Apache
Major >>> Server: Apache/2
Minor >>> Server: Apache/2.0
Minimal >>> Server: Apache/2.0.55
OS >>> Server: Apache/2.0.55 (Debian)
Full (or not specified) default >>> Server: Apache/2.0.55 (Debian) PHP/5.1.2-1+b1 mod_ssl/2.0.55 OpenSSL/0.9.8b
ServerSignature
控制由系統生成的頁面(錯誤訊息,mod_proxy ftp directory listing等等)的頁腳中如何顯示訊息。
可在全局設定檔案中控制,或是通過.htaccess檔案控制
預設為”off”(ServerSignature Off),有些Linux發行版本可能會開啟這個閥門,比如Debian在預設的虛擬主電腦上預設將這個閥門設定為開放
全局閥門的閥值會被虛擬主電腦或目錄單位的配置檔案中的閥值所覆蓋,所以,必須確保這樣的事情不應該發生
可用的閥值為下面所示:
Off (default): 不輸出任何頁腳訊息 (如同Apache1.2以及更舊版本,用於迷惑)
On:輸出一行關於版本號以及處於運行中的虛擬主電腦的ServerName (2.0.44之後的版本,由ServerTokens負責是否輸出版本號)
EMail: 建立一個傳輸到給ServerAdmin的”mailto”
注意:上述關於如何設定2個閥門從而盡量減少敏感訊息洩露的方法,並不會使你的伺服器真的更安全,如果你現在使用的版本比較舊,請務必盡快將系統升級,降低被蠕蟲攻擊的風險。
PHP程式設計問題諮詢:
請問,資料放入資料庫和取出來顯示在頁面需要注意什麼?
———————————————————
PHP程式設計問題回覆:
資料放入資料庫和取出來顯示在頁面需要注意什麼,可以參考以下的說明:
寫入資料庫時
$str=addslashes($str);
$sql="insert into `tab` (`content`) values('$str')";
輸出資料庫時
$str=stripslashes($str);
顯示資料庫內容時
$str=htmlspecialchars(nl2br($str)) ;
如何讀取當前位址欄資訊?可以參考以下的說明:
<?php
$s="http://{$_SERVER['HTTP_HOST']}:{$_SERVER["SERVER_PORT"]}{$_SERVER['SCRIPT_NAME']}";
$se='';
foreach ($_GET as $key => $value) {
$se.=$key."=".$value."&";
}
$se=Preg_Replace("/(.*)&$/","$1",$se);
$se?$se="?".$se:"";
echo $s."?$se";
?>
MacBook入手了,配置工作環境,首先得讓Mac OS支援PHP。不管你是採用整合的開發環境,比如XAMPP for Mac OS X,還是採用Mac OS中自帶的Apache和PHP,甚至自己重新編譯安裝,減少麻煩的第一步就是啟用root使用者。
本文采用的模式是使用Mac OS X 10.5.6自帶的Apache和PHP,安裝MySQL的dmg版本,以下操作非特殊說明均以root使用者在指令行下進行。
啟用root使用者
啟用Apache
現在Apache的主目錄就是/Libary/WebServer/Documents/,你可以在這目錄裡放置檔案測試了。
啟用PHP
修改/etc/apache2/httpd.conf中的
然後將/etc/php.ini.default複製為/etc/php.ini。
之後就可以按照自己的習慣修改php.ini的配置
最後,重啟Apache,可以在/Libary/WebServer/Documents/目錄中建立個phpinfo.php來測試了。
|