regex可以靜態編譯嗎
A. 在正則表達式中\\.和.有什麼區別
一、作為java的轉義字元
1.在Java中,反斜杠(\)是一個特殊的字元,被稱為轉義字元,它的作用是用來轉義後面一個字元。轉義後的字元通常用於表示一個不可見的字元或具有特殊含義的字元,例如換行(\n)、回車符(\r)、製表符(\t)。
2.在Java中以下字元都有特殊意義,無法直接表示
單引號:char c = 'a'; 表示字元類型的數據時需要使用單引號將字元左右括起來。所以要表示字元'則需要使用\'
雙引號:String str = "abc"; 表示字元串類型的數據時需要使用雙引號將字元串左右括起來。要表示字元串"則需要\"
反斜杠:String regex = "你好\n\t阿"; 在Java代碼中\表示轉義字元,所以如果要表示字面意思的\,則需要使用\\
所以用反斜杠加上本身字元來進行表示。
二、在正則表達式中
2.1Java中正則表達式的\
\表示將下一字元標記為特殊字元。如\d表示數字字元匹配,等效於 [0-9]。\w表示匹配任何字類字元(字母數字下劃線),注意包括下劃線。與"[A-Za-z0-9_]"等效。
在其他語言中,\\ 表示:我想要在正則表達式中插入一個普通的(字面上的)反斜杠,請不要給它任何特殊的意義。
在 Java 中,\\ 表示:我要插入一個正則表達式的反斜線,所以其後的字元具有特殊的意義。
在 Java 中,\\ 表示:我要插入一個正則表達式的反斜線,所以其後的字元具有特殊的意義。
在 Java 中,\\ 表示:我要插入一個正則表達式的反斜線,所以其後的字元具有特殊的意義。
\\中的第一個\表示java的轉義字元\由編譯器解析,第二個\是正則表達式\由正則表達式引擎解析。
所以,在其他的語言中(如Perl),一個反斜杠 \ 就足以具有轉義的作用,而在 Java 中正則表達式中則需要有兩個反斜杠才能被解析為其他語言中的轉義作用。也可以簡單的理解在 Java 的正則表達式中,兩個 \\ 代表其他語言中的一個 \,這也就是為什麼表示一位數字的正則表達式是 \\d,而表示一個普通的反斜杠是 \\\\。
所以Java正則表達式中匹配一個普通的反斜杠是\\\\。
所以如果在[]內表示一個],要寫兩個\,即[\\]]。
例如,我要在前面不是0-9,也不是) ] }三個反括弧的後面位置中,匹配 - 的後面是數字或者正括弧( [ { 的 - 前面的位置,須寫成:
(?<![0-9)}\\]])(?=-[0-9({\\[]) 。
若在該位置加0,可寫成String s = str.replaceAll("(?<![0-9)}\\]])(?=-[0-9({\\[]) ","0");
2.2說明:
字元 說明
^ 匹配輸入字元串開始的位置。如果設置了 RegExp 對象的 Multiline 屬性,^ 還會與"\n"或"\r"之後的位置匹配。
$ 匹配輸入字元串結尾的位置。如果設置了 RegExp 對象的 Multiline 屬性,$ 還會與"\n"或"\r"之前的位置匹配。
* 零次或多次匹配前面的字元或子表達式。例如,zo* 匹配"z"和"zoo"。* 等效於 {0,}。
+ 一次或多次匹配前面的字元或子表達式。例如,"zo+"與"zo"和"zoo"匹配,但與"z"不匹配。+ 等效於 {1,}。
? 零次或一次匹配前面的字元或子表達式。例如,"do(es)?「匹配"do"或"does"中的"do」。? 等效於 {0,1}。
{n} n 是非負整數。正好匹配 n 次。例如,"o{2}"與"Bob"中的"o"不匹配,但與"food"中的兩個"o"匹配。
{n,} n 是非負整數。至少匹配 n 次。例如,"o{2,}「不匹配"Bob"中的"o」,而匹配"foooood"中的所有 o。"o{1,}「等效於"o+」。"o{0,}「等效於"o*」。
{n,m} m 和 n 是非負整數,其中 n <= m。匹配至少 n 次,至多 m 次。例如,"o{1,3}"匹配"fooooood"中的頭三個 o。『o{0,1}』 等效於 『o?』。注意:您不能將空格插入逗號和數字之間。
x y
[xyz] 字元集。匹配包含的任一字元。例如,"[abc]「匹配"plain"中的"a」。
[ ^xyz] 反向字元集。匹配未包含的任何字元。例如,"[^abc]「匹配"plain"中"p」,「l」,「i」,「n」。
[a-z] 字元范圍。匹配指定范圍內的任何字元。例如,"[a-z]"匹配"a"到"z"范圍內的任何小寫字母。
\d 數字字元匹配。等效於 [0-9]。
\D 非數字字元匹配。等效於 [ ^0-9]。
\w 匹配任何字類字元,包括下劃線。與"[A-Za-z0-9_]"等效。
\W 與任何非單詞字元匹配。與"[ ^A-Za-z0-9_]"等效。
2.3Java正則表達式的使用方法
使用正則表達式需要引入 java.util.regex 包,我們就從這里入手講解:
java.util.regex
java.util.regex 包主要包括以下三個類:
Pattern 類:
pattern 對象是一個正則表達式的編譯表示。Pattern 類沒有公共構造方法。要創建一個 Pattern 對象,你必須首先調用其公共靜態編譯方法,它返回一個 Pattern 對象。該方法接受一個正則表達式作為它的第一個參數。
Matcher 類:
Matcher 對象是對輸入字元串進行解釋和匹配操作的引擎。與Pattern 類一樣,Matcher 也沒有公共構造方法。你需要調用 Pattern 對象的 matcher 方法來獲得一個 Matcher 對象。
matches方法
PatternSyntaxException:
PatternSyntaxException 是一個非強制異常類,它表示一個正則表達式模式中的語法錯誤
B. C#在文本中查找字元串
摘要:本文給出了在C#下利用正則表達式實現字元串搜索功能的方法,通過對.NET框架下的正則表達式的研究及實例分析,總結了正則表達式的元字元、規則、選項等。
關鍵字:正則表達式、元字元、字元串、匹配
1、正則表達式簡介
正則表達式提供了功能強大、靈活而又高效的方法來處理文本。正則表達式的全面模式匹配表示法可以快速地分析大量的文本以找到特定的字元模式;提取、編輯、替換或刪除文本子字元串;或將提取的字元串添加到集合以生成報告。對於處理字元串(例如 HTML處理、日誌文件分析和 HTTP 標頭分析)的許多應用程序而言,正則表達式是不可缺少的工具。
.NET 框架正則表達式並入了其他正則表達式實現的最常見功能,被設計為與 Perl 5 正則表達式兼容,.NET 框架正則表達式還包括一些在其他實現中尚未提供的功能,.NET 框架正則表達式類是基類庫的一部分,並且可以和面向公共語言運行庫的任何語言或工具一起使用。
2、字元串搜索
正則表達式語言由兩種基本字元類型組成:原義(正常)文本字元和元字元。正是元字元組為正則表達式提供了處理能力。當前,所有的文本編輯器都有一些搜索功能,通常可以打開一個對話框,在其中的一個文本框中鍵入要定位的字元串,如果還要同時進行替換操作,可以鍵入一個替換字元串,比如在Windows操作系統中的記事本、Office系列中的文檔編輯器都有這種功能。這種搜索最簡單的方式,這類問題很容易用String類的 String.Replace()方法來解決,但如果需要在文檔中識別某個重復的,該怎麼辦?編寫一個常式,從一個String類中選擇重復的字是比較復雜的,此時使用語言就很適合。
一般表達式語言是一種可以編寫搜索表達式的語言。在該語言中,可以把文檔中要搜索的文本、轉義序列和特定含義的其他字元組合在一起,例如序列\b表示一個字的開頭和結尾(子的邊界),如果要表示正在查找的以字元th開頭的字,就可以編寫一般表達式\bth(即序列字元界是-t-h)。如果要搜索所有以th結尾的字,就可以編寫th\b(序列t-h-字邊界)。但是,一般表達式要比這復雜得多,例如,可以在搜索操作中找到存儲部分文本的工具性程序(facility)。
3、.NET 框架的正則表達式類
下面通過介紹 .NET 框架的正則表達式類,熟悉一下.NET框架下的正則表達式的使用方法。
3.1 Regex 類表示只讀正則表達式
Regex 類包含各種靜態方法,允許在不顯式實例化其他類的對象的情況下使用其他正則表達式類。以下代碼示例創建了 Regex 類的實例並在初始化對象時定義一個簡單的正則表達式。請注意,使用了附加的反斜杠作為轉義字元,它將 \s 匹配字元類中的反斜杠指定為原義字元。
Regex r; // 聲明一個 Regex類的變數
r = new Regex("\\s2000"); // 定義表達式
3.2 Match 類表示正則表達式匹配操作的結果
以下示例使用 Regex 類的 Match 方法返回 Match 類型的對象,以便找到輸入字元串中第一個匹配。此示例使用 Match 類的 Match.Success 屬性來指示是否已找到匹配。
Regex r = new Regex("abc"); // 定義一個Regex對象實例
Match m = r.Match("123abc456"); // 在字元串中匹配
if (m.Success)
{
Console.WriteLine("Found match at position " + m.Index); //輸入匹配字元的位置
}
3.3 MatchCollection 類表示非重疊匹配的序列
該集合為只讀的,並且沒有公共構造函數。MatchCollection 的實例是由 Regex.Matches 屬性返回的。使用 Regex 類的 Matches 方法,通過在輸入字元串中找到的所有匹配填充 MatchCollection。下面代碼示例演示了如何將集合復制到一個字元串數組(保留每一匹配)和一個整數數組(指示每一匹配的位置)中。
MatchCollection mc;
String[] results = new String[20];
int[] matchposition = new int[20];
Regex r = new Regex("abc"); //定義一個Regex對象實例
mc = r.Matches("123abc4abcd");
for (int i = 0; i < mc.Count; i++) //在輸入字元串中找到所有匹配
{
results[i] = mc[i].Value; //將匹配的字元串添在字元串數組中
matchposition[i] = mc[i].Index; //記錄匹配字元的位置
}
3.4 GroupCollection 類表示捕獲的組的集合
該集合為只讀的,並且沒有公共構造函數。GroupCollection 的實例在 Match.Groups 屬性返回的集合中返回。下面的控制台應用程序查找並輸出由正則表達式捕獲的組的數目。
using System;
using System.Text.RegularExpressions;
public class RegexTest
{
public static void RunTest()
{
Regex r = new Regex("(a(b))c"); //定義組
Match m = r.Match("abdabc");
Console.WriteLine("Number of groups found = " + m.Groups.Count);
}
public static void Main()
{
RunTest();
}
}
該示例產生下面的輸出:
Number of groups found = 3
3.5 CaptureCollection 類表示捕獲的子字元串的序列
由於限定符,捕獲組可以在單個匹配中捕獲多個字元串。Captures屬性(CaptureCollection 類的對象)是作為 Match 和 group 類的成員提供的,以便於對捕獲的子字元串的集合的訪問。例如,如果使用正則表達式 ((a(b))c)+(其中 + 限定符指定一個或多個匹配)從字元串"abcabcabc"中捕獲匹配,則子字元串的每一匹配的 Group 的 CaptureCollection 將包含三個成員。
下面的程序使用正則表達式 (Abc)+來查找字元串"XYZAbcAbcAbcXYZAbcAb"中的一個或多個匹配,闡釋了使用 Captures 屬性來返回多組捕獲的子字元串。
using System;
using System.Text.RegularExpressions;
public class RegexTest
{
public static void RunTest()
{
int counter;
Match m;
CaptureCollection cc;
GroupCollection gc;
Regex r = new Regex("(Abc)+"); //查找"Abc"
m = r.Match("XYZAbcAbcAbcXYZAbcAb"); //設定要查找的字元串
gc = m.Groups;
//輸出查找組的數目
Console.WriteLine("Captured groups = " + gc.Count.ToString());
// Loop through each group.
for (int i=0; i < gc.Count; i++) //查找每一個組
{
cc = gc[i].Captures;
counter = cc.Count;
Console.WriteLine("Captures count = " + counter.ToString());
for (int ii = 0; ii < counter; ii++)
{
// Print capture and position.
Console.WriteLine(cc[ii] + " Starts at character " +
cc[ii].Index); //輸入捕獲位置
}
}
}
public static void Main() {
RunTest();
}
}
此例返回下面的輸出結果:
Captured groups = 2
Captures count = 1
AbcAbcAbc Starts at character 3
Captures count = 3
Abc Starts at character 3
Abc Starts at character 6
Abc Starts at character 9
3.6 Capture 類包含來自單個子表達式捕獲的結果
在 Group 集合中循環,從 Group 的每一成員中提取 Capture 集合,並且將變數 posn 和 length 分別分配給找到每一字元串的初始字元串中的字元位置,以及每一字元串的長度。
Regex r;
Match m;
CaptureCollection cc;
int posn, length;
r = new Regex("(abc)*");
m = r.Match("bcabcabc");
for (int i=0; m.Groups[i].Value != ""; i++)
{
cc = m.Groups[i].Captures;
for (int j = 0; j < cc.Count; j++)
{
posn = cc[j].Index; //捕獲對象位置
length = cc[j].Length; //捕獲對象長度
}
}
把組合字元組合起來後,每次都會返回一個組對象,就可能並不是我們希望的結果。如果希望把組合字元作為搜索模式的一部分,就會有相當大的系統開銷。對於單個的組,可以用以字元序列"?:"開頭的組禁止這么做,就像URI樣例那樣。而對於所有的組,可以在RegEx.Matches()方法上指定 RegExOptions.ExplicitCapture標志。
4、利用正則表達式實現字元串搜索
4.1 在C#中使用.NET一般表達式引擎
下面將通過一個樣例的開發,執行並顯示一些搜索的結果,說明一般表達式的一些特性,以及如何在C#中使用.NET一般表達式引擎。說明使用字元串時應在前面加上符號@。
String Text=@"I can not find my position in Beijing";
把這個文本稱為輸入字元串,為了說明一般表達式.NET類,本文先進行一次純文本的搜索,這次搜索不帶任何轉義序列或一般表達式命令。假定要查找所有字元串ion,把這個搜索字元串稱為模式。使用一般表達式和上面聲明的變數Text,編寫出下面的代碼:
String Pattern = "ion";
MatchCollection Matches = Regex.Matches(Text,Pattern,RegexOptions);
foreach(Match NextMatch in Matches)
{ Console.WriteLine(NextMatch.Index); }
在這段代碼中,使用了System.Text.RegularExpressions名稱空間中Regex類的靜態方法Match()。這個方法的參數是一些輸入文本、一個模式和RegexOptions每句中的一組可選標志。Matches()返回MatchCollection,每個匹配都用一個 Match對象來表示。在上面的代碼中,只是在集合中迭代,使用Match類的Index屬性,返回輸入文本中匹配所在的索引。運行這段代碼,將得到1個匹配項。
一般集合的功能主要取決於模式字元串。原因是模式字元串不僅僅包含純文本。如前所述。還包含元字元和轉義序列,元字元是給出命令的特殊字元,而轉義序列的工作方式與C#的轉義序列相同,它們都是以反斜杠\開頭的字元,具有特殊的含義。例如,假定要查找以n開頭的字,就可以使用轉義序列\b,它表示一個字的邊界(字的邊界是以某個字母數字標的字元開頭,或者後面是一個空白字元或標點符號),下面編寫如下代碼:
String Pattern = @"\bn";
MatchCollection Matches = Regex.Matches(Text,Pattern,RegexOptions.IgnoreCase
RegexOptions.ExplicitCapture);
要在運行時把\b傳遞給.NET一般表達式引擎,反斜杠\不應被C#編譯器解釋為轉義序列。如果要查找以序列ion結尾的字,可以使用下面的代碼:
String Pattern = @"ion\b";
如果要查找以字母n開頭,以序列ion結尾的所有字,需要一個以\bn開頭,以ion\b結尾的模式,中間內容怎麼辦?需要告訴計算機n和ion中間的內容可以是任意長度的字元,只要字元不是空白即可,正確的模式如下所示:
String Pattern = @"\bn\S*ion\b";
4.2 特定字元或轉義序列
大多數重要的正則表達式語言運算符都是非轉義的單個字元。轉義符 \(單個反斜杠)通知正則表達式分析器反斜杠後面的字元不是運算符。例如,分析器將星號 (*) 視為重復限定符,而將後跟星號的反斜杠 (\*) 視為 Unicode 字元 002A。
使用一般表達式要習慣的一點是,查看像這樣怪異的字元序列,但這個序列的工作是非常邏輯化的。轉義序列\S表示任何不適空白的字元。*稱為數量詞,其含義是前面的字元可以重復任意次,包括0次。序列\S*表示任何不適空白的字元。因此,上面的模式匹配於以n開頭,以ion結尾的任何單個字。下表中列出的字元轉義在正則表達式和替換模式中都會被識別。
表1:特定字元或轉義序列
特定字元或轉義序列 含義 樣例 匹配的樣例
^ 輸入文本的開頭 ^B B,但只能是文本中的第一個字元
$ 輸入文本的結尾 X$ X,但只能是文本中的最後一個字元
. 除了換行字元(\n)以外的所有單個字元 i.ation isation、ization
* 可以重復0次或多次的前導字元 ra*t rat、raat等
+ 可以重復1次或多次的前導字元 ra+t rt、rat、raat等
? 可以重復0次或1次的前導字元 ra?t 只有rt和rat匹配
\s 任何空白字元 \sa [space]a,\ta,\na(\t和\n與C#的\t和\n含義相同)
\S 任何不是空白的字元 \SF aF,rF,cF,但不能是\tf
\b 字邊界 ion\b 以ion結尾的任何字
\B 不是字邊界的位置 \BX\B 字中間的任何X
如果要搜索一個元字元,也可以通過帶有反斜杠的轉義字元來表示。例如,.表示除了換行字元以外的任何字元,而\.表示一個點。
可以把可替換的字元放在方括弧中,請求匹配包含這些字元。例如,[1 c]表示字元可以是1或者是c。如果要搜索map或者man,可以使用序列"ma[n p]"(僅指引號內字元,下面雷同)。在方括弧中,也可以制定一個范圍,例如"[a-z]"表示所有的小寫字母(使用連字型大小 (-) 允許指定連續字元范圍),"[B-F]"表示B到F之間的所有大寫字母,"[0-9]"表示一個數字,如果要搜索一個整數(該序列只包含0到9的字元),就可以編寫"[0-9]+"(注意,使用+字元表示至少要有這樣一個數字,但可以有多個數字,所以9、83和3443等都是匹配的。)
下面看看一般表達式的結果,編寫一個實例RegularExpressionsZzy。建立幾個一般表達式,顯示其結果,讓用戶了解一下表達式是如何工作的。
該實例的核心是一個方法WriteMatches(),它把MatchCollection中的所有匹配以比較詳細的方式顯示出來。對於每個匹配,它都會顯示該匹配在輸入字元串中所在的索引,匹配的字元串和一個略長的字元串,其中包含輸入文本中至多8個外圍字元,其中至少有5個字元放在匹配的前面,至多5個字元放在匹配的後面(如果匹配的位置在輸入文本的開頭或結尾5個字元內,則結果中匹配前後的字元就會少於4個)。換言之,靠近輸入文本末尾的匹配應是"and messaging ofd",匹配的前後各有5個字元,但位於輸入文本的最後一個字上的匹配就應是"g of data",匹配的字後只有一個字元。因為在該字元的後面是字元串的結尾。這個長字元串可以更清楚地表明一般表達式是在什麼地方查找到匹配的:
static void WriteMatches(string text, MatchCollection matches)
{
Console.WriteLine("Original text was: \n\n" + text + "\n");
Console.WriteLine("No. of matches: " + matches.Count);
foreach (Match nextMatch in matches)
{
int Index = nextMatch.Index;
string result = nextMatch.ToString();
int charsBefore = (Index < 5) ? Index : 5;
int fromEnd = text.Length - Index - result.Length;
int charsAfter = (fromEnd < 5) ? fromEnd : 5;
int charsToDisplay = charsBefore + charsAfter + result.Length;
Console.WriteLine("Index: {0}, \tString: {1}, \t{2}",Index, result,
text.Substring(Index - charsBefore, charsToDisplay));
}
}
在這個方法中,處理過程是確定在較長的字元串中有多少個字元可以顯示,而無需超限輸入文本的開頭或結尾。注意在Match對象上使用了另一個屬性Value,它包含標識該匹配的字元串,而且,RegularExpressionsZzy只包含名為Find_po,Find_n等的方法,這些方法根據本文執行某些搜索操作。
4.3 正則表達式選項
可以使用影響匹配行為的選項修改正則表達式模式。可以通過兩種基本方法設置正則表達式選項:其一是可以在 Regex(pattern, options) 構造函數中的 options 參數中指定,其中 options 是 RegexOptions 枚舉值的按位"或"組合;其二是使用內聯 (?imnsx-imnsx:) 分組構造或 (?imnsx-imnsx) 其他構造在正則表達式模式內設置它們。
在內聯選項構造中,一個選項或一組選項前面的減號 (-) 用於關閉這些選項。例如,內聯構造 (?ix-ms) 將打開 IgnoreCase 和 IgnorePatternWhiteSpace 選項而關閉 Multiline 和 Singleline 選項。
表2:RegexOptions 枚舉的成員以及等效的內聯選項字元
RegexOption 成員 內聯字元 說明
None 無 指定不設置任何選項。
IgnoreCase i 指定不區分大小寫的匹配。
Multiline m 指定多行模式。更改 ^ 和 $ 的含義,以使它們分別與任何行的開頭和結尾匹配,而不只是與整個字元串的開頭和結尾匹配。
ExplicitCapture n 指定唯一有效的捕獲是顯式命名或編號的 (?...) 形式的組。這允許圓括弧充當非捕獲組,從而避免了由 (?:...) 導致的語法上的笨拙。
Compiled 無 指定正則表達式將被編譯為程序集。生成該正則表達式的 Microsoft 中間語言 (MSIL) 代碼;以較長的啟動時間為代價,得到更快的執行速度。
Singleline s 指定單行模式。更改句點字元 (.) 的含義,以使它與每個字元(而不是除 \n 外的所有字元)匹配。
IgnorePatternWhitespace x 指定從模式中排除非轉義空白並啟用數字元號 (#) 後面的注釋。請注意,空白永遠不會從字元類中消除。
RightToLeft 無 指定搜索是從右向左而不是從左向右進行的。具有此選項的正則表達式將移動到起始位置的左邊而不是右邊。(因此,起始位置應指定為字元串的結尾而不是開頭。)為了避免構造具有無限循環的正則表達式的可能性,此選項不能在中流指定。但是,(?<) 回顧後發構造提供了可用作子表達式的類似替代物。
ECMAScript 無 指定已為表達式啟用了符合 ECMAScript 的行為。此選項僅可與 IgnoreCase 和 Multiline 標志一起使用。將 ECMAScript 同任何其他標志一起使用將導致異常。
例如,Find_po在字開頭處查找以"po"開頭的字元串:
static void Find_po()
{
string text = @" I can not find my position in Beijing ";
string pattern = @"\bpo\S*ion\b";
MatchCollection matches = Regex.Matches(text, pattern, RegexOptions.IgnoreCase
RegexOptions.IgnorePatternWhitespace RegexOptions.ExplicitCapture);
WriteMatches(text, matches);
}
這段代碼還使用了名稱空間RegularExpressions:
using System;
using System.Text.RegularExpressions;
4.4 匹配、組和捕獲
一般表達式的一個很好的特性是可以把字元組合起來,方式與C#中的復合語句一樣。在C#中,可以通過把任意數量的語句放在花括弧中的方式把它們組合在一起。其結果就像一個復合語句那樣。在一般表達式模式中,也可以把任何字元組合起來(包括元字元和轉義序列),像處理一個字元那樣處理它們。唯一的區別是要使用圓括弧,而不是花括弧,得到的序列成為一個組。
例如,模式"(an)+"定位序列an的任以重復。量詞+只應用於它前面的一個字元,但因為我們把字元組合起來了,所以它現在把重復的an作為一個單元來對待。"(an)."應用到輸入文本"bananas came to Europe late in the annals of history"上,會從bananas中選擇出anan。另一方面,如果使用an+,則將從annals中選擇ann,從bananas中選擇出兩個 an。為什麼(an)+選擇的是anan,而沒有把單個的an作為一個匹配。匹配規則是不能重復的,如果有可能重復,在默認情況下就選擇較長的匹配。
但是,組的功能要比這強大得多。在默認情況下,把模式的一部分組合為一個組時,就要求一般表達式引擎記住可以按照這個組來匹配,也可以按照整個模式來匹配。換言之,可以把組當作一個要匹配的模式,如果要把字元串分解為各個部分,這種模式就是非常有效的。
例如,URI的格式是" :// : ",其中埠是可選的。它的一個樣例是http://www.comprg.com.cn:8080。假定要從一個URI中提取協議、地址和埠,而且緊鄰URI的後面可能有空白(但沒有標點符號),就可以使用下面的表達式:"\b(\S+)://(\S+)(?::(\S+))?\b"
該表達式的工作方式如下:首先,前導和尾部的\b序列確保只需要考慮完全是字的文本部分,在這個文本部分中,第一組"(\S+)://"會選擇一個或多個不適空白的字元,其後是"://"。在HTTPURI的開頭會選擇出http://。花括弧表示把http存儲為一個組。後面的"(\S+)"則在上述URI中選擇www. comprg.com.cn,這個組在遇到詞的結尾時或標記另一個組的冒號"(:)"時結束。
下一個組選擇埠(本例是:8080)。後面的?表示這個組在匹配中是可選的,如果沒有:xxxx,也不會妨礙匹配的標記。
這是非常重要的,因為埠在URI中一般不指定,實際上,在大多數情況下,URI是沒有埠號的。但是,事情會比較復雜。如果要求冒號可以出現,也可以不出現,但不希望把這個冒號也存儲在組中。為此,可以嵌套兩個組:內部的"(\S+)"組選擇冒號後面的內容(本例中是8080),外面的組包含內部的組,後面是一個冒號,該冒號又在序列"?:"的後面。這個序列表示該組不應保存(只需要保存"8080",不需要保存":8080")。不要把這兩個冒號混淆了,第一個冒號是序列"?:"的一部分,表示不保存這個組,第二個冒號是要搜索的文本。
在這個字元串上運行該模式:I always visit http://www. comprg.com.cn 得到的匹配是http://www. comprg.com.cn。在這個匹配中,僅提到了三個組,還有第四個組表示匹配本身。理論上,每個組都可以選擇0次、1次或者多次匹配。單個的匹配就稱為捕獲。在第一個組"(\S+)",有一個捕獲http。第二個組也有一個捕獲www. comprg.com.cn,但第三個組沒有捕獲,因為在這個URI中沒有埠號。注意該字元串在其本身上包含第二個http://。雖然它匹配於第一個組,但不會被搜索出來,因為整個搜索表達式不匹配於這部分文本。
再比如下面這個例子,以下代碼示例使用 Match.Result 來從 URL提取協議和埠號。例如,"http://www.yahoo.com.cn:8080/index.html"將返回"http:8080"。
String Extension(String url)
{
Regex r = new Regex(@"^(? \w+)://[^/]+?(? :\d+)?/",
RegexOptions.Compiled);
return r.Match(url).Result("${proto}${port}");
}
5、小結
.NET 框架正則表達式類是基類庫的一部分,並且可以和面向公共語言運行庫的任何語言或工具(包括 ASP.NET和 Visual Studio .NET)一起使用。本文給出了在C#下利用正則表達式實現字元串搜索功能的方法,通過對.NET框架下的正則表達式的研究及實例分析,總結了正則表達式的規則、選項等,方便以後朋友們的應用。
C. 如何使用java.util.regex包
在Sun的Java JDK 1.40版本中,Java自帶了支持正則表達式的包,本文就拋磚引玉地介紹了如何使用java.util.regex包。
可粗略估計一下,除了偶爾用Linux的外,其他Linu x用戶都會遇到正則表達式。正則表達式是個極端強大工具,而且在字元串模式-匹配和字元串模式-替換方面富有彈性。在Unix世界裡,正則表達式幾乎沒有什麼限制,可肯定的是,它應用非常之廣泛。
正則表達式的引擎已被許多普通的Unix工具所實現,包括grep,awk,vi和Emacs等。此外,許多使用比較廣泛的腳本語言也支持正則表達式,比如Python,Tcl,JavaScript,以及最著名的Perl。
我很早以前就是個Perl方面的黑客,如果你和我一樣話,你也會非常依賴你手邊的這些強大的text-munging工具。近幾年來,像其他程序開發者一樣,我也越來越關注Java的開發。
Java作為一種開發語言,有許多值得推薦的地方,但是它一直以來沒有自帶對正則表達式的支持。直到最近,藉助於第三方的類庫,Java開始支
持正則表達式,但這些第三方的類庫都不一致、兼容性差,而且維護代碼起來很糟糕。這個缺點,對我選擇Java作為首要的開發工具來說,一直是個巨大的顧慮
之處。
你可以想像,當我知道Sun的Java JDK
1.40版本包含了java.util.regex(一個完全開放、自帶的正則表達式包)時,是多麼的高興!很搞笑的說,我花好些時間去挖掘這個被隱藏起
來的寶石。我非常驚奇的是,Java這樣的一個很大改進(自帶了java.util.regex包)為什麼不多公開一點呢?!
最近,Java雙腳都跳進了正則表達式的世界。java.util.regex包在支持正則表達也有它的過人之處,另外Java也提供詳細的相
關說明文檔。使得朦朦朧朧的regex神秘景象也慢慢被撥開。有一些正則表達式的構成(可能最顯著的是,在於糅合了字元類庫)在Perl都找不到。
在regex包中,包括了兩個類,Pattern(模式類)和Matcher(匹配器類)。Pattern類是用來表達和陳述所要搜索模式的對
象,Matcher類是真正影響搜索的對象。另加一個新的例外類,PatternSyntaxException,當遇到不合法的搜索模式時,會拋出例
外。
即使對正則表達式很熟悉,你會發現,通過java使用正則表達式也相當簡單。要說明的一點是,對那些被Perl的單行匹配所寵壞的Perl狂熱愛好者來說,在使用java的regex包進行替換操作時,會比他們所以前常用的方法費事些。
本文的局限之處,它不是一篇正則表達式用法的完全教程。如果讀者要對正則表達進一步了解的話,推薦閱讀Jeffrey
Frieldl的Mastering Regular
Expressions,該書由O』Reilly出版社出版。我下面就舉一些例子來教讀者如何使用正則表達式,以及如何更簡單地去使用它。
設計一個簡單的表達式來匹配任何電話號碼數字可能是比較復雜的事情,原因在於電話號碼格式有很多種情況。所有必須選擇一個比較有效的模式。比如:(212) 555-1212, 212-555-1212和212 555 1212,某些人會認為它們都是等價的。
首先讓我們構成一個正則表達式。為簡單起見,先構成一個正則表達式來識別下面格式的電話號碼數字:(nnn)nnn-nnnn。
第一步,創建一個pattern對象來匹配上面的子字元串。一旦程序運行後,如果需要的話,可以讓這個對象一般化。匹配上面格式的正則表達可以
這樣構成:(\d{3})\s\d{3}-\d{4},其中\d單字元類型用來匹配從0到9的任何數字,另外{3}重復符號,是個簡便的記號,用來表示有
3個連續的數字位,也等效於(\d\d\d)。\s也另外一個比較有用的單字元類型,用來匹配空格,比如Space鍵,tab鍵和換行符。
是不是很簡單?但是,如果把這個正則表達式的模式用在java程序中,還要做兩件事。對java的解釋器來說,在反斜線字元
(\)前的字元有特殊的含義。在java中,與regex有關的包,並不都能理解和識別反斜線字元(\),盡管可以試試看。但為避免這一點,即為了讓反斜
線字元(\)在模式對象中被完全地傳遞,應該用雙反斜線字元(\)。此外圓括弧在正則表達中兩層含義,如果想讓它解釋為字面上意思(即圓括弧),也需要在
它前面用雙反斜線字元(\\)。也就是像下面的一樣:
\\(\\d{3}\\)\\s\\d{3}-\\d{4}
現在介紹怎樣在java代碼中實現剛才所講的正則表達式。要記住的事,在用正則表達式的包時,在你所定義的類前需要包含該包,也就是這樣的一行:
import java.util.regex.*;
下面的一段代碼實現的功能是,從一個文本文件逐行讀入,並逐行搜索電話號碼數字,一旦找到所匹配的,然後輸出在控制台。
BufferedReader in;
Pattern pattern = Pattern.compile("\\(\\d{3}\\)\\s\\d{3}-\\d{4}");
in = new BufferedReader(new FileReader("phone"));
String s;
while ((s = in.readLine()) != null)
{
Matcher matcher = pattern.matcher(s);
if (matcher.find())
{
System.out.println(matcher.group());
}
}
in.close();
對那些熟悉用Python或Javascript來實現正則表達式的人來說,這段代碼很平常。在Python和Javascript這些語言
中,或者其他的語言,這些正則表達式一旦明確地編譯過後,你想用到哪裡都可以。與Perl的單步匹配相比,看起來多多做了些工作,但這並不很費事。
find()方法,就像你所想像的,用來搜索與正則表達式相匹配的任何目標字元串,group()方法,用來返回包含了所匹配文本的字元串。應
注意的是,上面的代碼,僅用在每行只能含有一個匹配的電話號碼數字字元串時。可以肯定的說,java的正則表達式包能用在一行含有多個匹配目標時的搜索。
本文的原意在於舉一些簡單的例子來激起讀者進一步去學習java自帶的正則表達式包,所以對此就沒有進行深入的探討。
這相當漂亮吧! 但是很遺憾的是,這僅是個電話號碼匹配器。很明顯,還有兩點可以改進。如果在電話號碼的開頭,即區位號和本地號碼之間可能會有空格。我們也可匹配這些情況,則通過在正則表達式中加入\s?來實現,其中?元字元表示在模式可能有0或1個空格符。
第二點是,在本地號碼位的前三位和後四位數字間有可能是空格符,而不是連字型大小,更有勝者,或根本就沒有分隔符,就是7位數字連在一起。對這幾種
情況,我們可以用(-|)?來解決。這個結構的正則表達式就是轉換器,它能匹配上面所說的幾種情況。在()能含有管道符|時,它能匹配是否含有空格符或連
字元,而尾部的?元字元表示是否根本沒有分隔符的情況。
最後,區位號也可能沒有包含在圓括弧內,對此可以簡單地在圓括弧後附上?元字元,但這不是一個很好的解決方法。因為它也包含了不配對的圓括弧,
比如"(555" 或
"555)"。相反,我們可以通過另一種轉換器來強迫讓電話號碼是否帶有有圓括弧:(\(\d{3}\)|\d{3})。如果我們把上面代碼中的正則表達
式用這些改進後的來替換的話,上面的代碼就成了一個非常有用的電話號碼數字匹配器:
Pattern pattern =
Pattern.compile("(\\(\\d{3}\\)|\\d{3})\\s?\\d{3}(-|)?\\d{4}");
可以確定的是,你可以自己試著進一步改進上面的代碼。
現在看看第二個例子,它是從Friedl的中改編過來的。其功能是用來檢查文本文件中是否有重復的單詞,這在印刷排版中會經常遇到,同樣也是個語法檢查器的問題。
匹配單詞,像其他的一樣,也可以通過好幾種的正則表達式來完成。可能最直接的是\b\w+\b,其優點在於只需用少量的regex元字元。其中
\w元字元用來匹配從字母a到u的任何字元。+元字元表示匹配匹配一次或多次字元,\b元字元是用來說明匹配單詞的邊界,它可以是空格或任何一種不同的標
點符號(包括逗號,句號等)。
現在,我們怎樣來檢查一個給定的單詞是否被重復了三次?為完成這個任務,需充分利用正則表達式中的所熟知的向後掃描。如前面提到的,圓括弧在正
則表達式中有幾種不同的用法,一個就是能提供組合類型,組合類型用來保存所匹配的結果或部分匹配的結果(以便後面能用到),即使遇到有相同的模式。在同樣
的正則表達中,可能(也通常期望)不止有一個組合類型。在第n個組合類型中匹配結果可以通過向後掃描來獲取到。向後掃描使得搜索重復的單詞非常簡
單:\b(\w+)\s+\1\b。
圓括弧形成了一個組合類型,在這個正則表示中它是第一組合類型(也是僅有的一個)。向後掃描\1,指的是任何被\w+所匹配的單詞。我們的正則
表達式因此能匹配這樣的單詞,它有一個或多個空格符,後面還跟有一個與此相同的單詞。注意的是,尾部的定位類型(\b)必不可少,它可以防止發生錯誤。如
果我們想匹配"Paris in the the spring",而不是匹配"Java's regex package is the theme
of this article"。根據java現在的格式,則上面的正則表達式就是:Pattern pattern
=Pattern.compile("\\b(\\w+)\\s+\\1\\b");
最後進一步的修改是讓我們的匹配器對大小寫敏感。比如,下面的情況:"The the theme of this article is
the Java's regex
package.",這一點在regex中能非常簡單地實現,即通過使用在Pattern類中預定義的靜態標志CASE_INSENSITIVE :
Pattern pattern =Pattern.compile("\\b(\\w+)\\s+\\1\\b",
Pattern.CASE_INSENSITIVE);
有關正則表達式的話題是非常豐富,而且復雜的,用Java來實現也非常廣泛,則需要對regex包進行的徹底研究,我們在這里所講的只是冰山一
角。即使你對正則表達式比較陌生,使用regex包後會很快發現它強大功能和可伸縮性。如果你是個來自Perl或其他語言王國的老練的正則表達式的黑客,
使用過regex包後,你將會安心地投入到java的世界,而放棄其他的工具,並把java的regex包看成是手邊必備的利器。