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包看成是手边必备的利器。