正则表达式

[color=#345286] 由于项目中需要用到正则表达式,再一每次使用正则表达式时都要查资料,很是繁琐。于是乎,急需把平时积累的知识总结出来作为读书笔记。该篇文章包括了正则表达式的基本知识的介绍,并附加了四五个有代表性的例子,它们均用 java 和 javascript 来实现(最后一个例子是求 ip 地址的正则表达式,只使用 javascript 来做例子。我看很多网站给出的表达式都是不准确的,该例子我测试过,并列出了得出正确结果前我所写的错误的表达式,并给出错误的原因),以体现它们之间的区别。写这篇文章的同时还参考了以下的文章:[/color]
[color=#345286] [url]http://edu.yesky.com/edupxpt/18/2143018.shtml[/url][/color]
[color=#345286] [url]http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm[/url][/color]

[color=#FF0000] [b][size=medium]● 句点符号[/size][/b][/color]
[color=#345286] 句点符号符号匹配所有字符,包括空格、Tab字符甚至换行符。正则表达式 [b]t.n[/b] 匹配 tan, ten, tin, ton, t#n, tpn, 甚至 t n,还有其它许多无意义的组合。[/color]

[color=#FF0000] [b][size=medium]● 方括号符号[/size][/b][/color]
[color=#345286] 为了解决句点符号匹配范围过于广泛这一问题,你可以在方括号("[]")里面指定看来有意义的字符。此时,只有方括号里面指定的字符才参与匹配。也就是说,正则表达式 [b]t[aeio]n[/b] 只匹配 tan, Ten, tin 和 ton。但 Toon 不匹配,因为在方括号之内你只能匹配单个字符。[/color]

[color=#FF0000] [b][size=medium]● 或符号[/size][/b][/color]
[color=#345286] 如果除了上面匹配的所有单词之外,你还想要匹配 toon,那么,你可以使用 "|" 操作符。"|" 操作符的基本意义就是"或"运算。要匹配 toon,使用 [b]t(a|e|i|o|oo)n[/b] 正则表达式。这里不能使用方扩号,因为方括号只允许匹配单个字符。这里必须使用圆括号 "()"。圆括号还可以用来分组,具体请参见后面介绍。[/color]

[color=#FF0000] [b][size=medium]● 量词[/size][/b][/color]
[color=#345286] 量词描述了一个模式吸收输入文本的方式:[/color]
[list=1]
[*] [color=#345286]贪婪的:量词总是贪婪的,除非有其它的选项被设置。贪婪表达式会为所有可能的模式发现尽可能多的匹配。导致此问题的一个典型理由就是假定我们的模式仅能匹配第一个可能的字符组,如果它确实是贪婪的,那么它就会继续往下匹配。[/color]
[*] [color=#345286]勉强的:用问号来指定,这个量词匹配满足模式所需的最少字符数。因此也称为懒惰的、最少匹配、非贪婪的或不贪婪的(lazy, minimal matching, non-greedy, or ungreedy)。[/color]
[*] [color=#345286]占有的:量词当前只有在 java 语言中才可用(在其它语言中不可用),并且它也更高级,因此我们大概不会立即用到它。当正则表达式被应用于字符串时,它会产生相当多的状态以便在匹配失败时可以回溯。而占有量词并不保存这些中间状态,因此它们可以防止回溯。它们常常用于防止正则表达式失控,因此可以使正则表达式执行起来更有效。[/color]
[/list][table]
| [color=#345286][b]Greey[/b][/color] | [color=#345286][b]Reluctant[/b][/color] | [color=#345286][b]Possessive[/b][/color] | [color=#345286][b]Matches[/b][/color] |
| [color=#345286]X?[/color] | [color=#345286]X??[/color] | [color=#345286]X?+[/color] | [color=#345286]X, 0 次或 1 次[/color] |
| [color=#345286]X*[/color] | [color=#345286]X*?[/color] | [color=#345286]X*+[/color] | [color=#345286]X, 0 次或多次[/color] |
| [color=#345286]X+[/color] | [color=#345286]X+?[/color] | [color=#345286]X++[/color] | [color=#345286]X, 1 次或多次。例如,对于字符串 "oooo",'o+?' 将匹配单个 "o",而 'o+' 将匹配所有 'o'。[/color] |
| [color=#345286]X{n}[/color] | [color=#345286]X{n}?[/color] | [color=#345286]X{n}+[/color] | [color=#345286]X, 恰好 n 次[/color] |
| [color=#345286]X{n,}[/color] | [color=#345286]X{n,}?[/color] | [color=#345286]X{n,}+ | X, 至少 n 次[/color] |
| [color=#345286]X{n,m}[/color] | [color=#345286]X{n,m}?[/color] | [color=#345286]X{n,m}+[/color] | [color=#345286]X, 至少 n 次,至多 m 次,即为闭集合,[n, m][/color] |
[/table]
[color=#345286] [b]①[/b] 假设要匹配类似这样的字符串:999-99-9999(连字符 "-" 在正则表达式中有特殊的含义,表示范围,因此在写正则表达式时需转义),我们可以使用如下图所示的正则表达式:[/color]
[img]http://dl.iteye.com/upload/attachment/144407/b072667d-fc71-3de2-b7fb-934ac13d92bb.gif[/img]
[color=#345286] [b]②[/b] 假设你希望连字符号可以出现,也可以不出现——即,999-99-9999 和 999999999 都属于正确的格式。这时,你可以在连字符号后面加上 "?" 数量限定符号,我们可以使用如下图所示的正则表达式:[/color]
[img]http://dl.iteye.com/upload/attachment/144963/982ac466-36d8-362c-bed8-dd2846588f47.gif[/img]
[color=#345286] [b]③[/b] 下面我们再来看另外一个例子。美国汽车牌照的一种格式是四个数字加上二个字母。它的正则表达式前面是数字部分 "[0-9]{4}",再加上字母部分 "[A-Z]{2}"。我们可以使用如下图所示的正则表达式:[/color]
[img]http://dl.iteye.com/upload/attachment/144971/e15f1289-4791-36b7-a39b-67d3cdcaed99.gif[/img]

[color=#FF0000] [b][size=medium]● "否" 符号(^)[/size][/b][/color]
[color=#345286] "^" 符号称为 "否" 符号。如果用在方括号内,"^" 表示不想要匹配的字符。例如,下图的正则表达式匹配所有单词,但以 "X" 字母开头的单词除外。[/color]
[img]http://dl.iteye.com/upload/attachment/146703/d9aa89d7-3785-3589-ac48-20bf5e29eeff.gif[/img]

[color=#FF0000] [b][size=medium]● 圆括号和空白符号[/size][/b][/color]
[color=#345286] 假设要从格式为 "June 26, 1951" 的生日日期中提取出月份部分,用来匹配该日期的正则表达式可以如下图所示:[/color]
[img]http://dl.iteye.com/upload/attachment/147066/05baa40c-bbc8-31ba-8895-fa3369d1e972.gif[/img]
[color=#345286] 新出现的 "\s" 符号是空白符号,匹配所有的空白字符,包括 Tab 字符。如果字符串正确匹配,接下来如何提取出月份部分呢?只需在月份周围加上一个圆括号创建一个组,然后用 ORO API(本文后面详细讨论)提取出它的值。修改后的正则表达式如下图所示:[/color]
[img]http://dl.iteye.com/upload/attachment/147068/e84934fc-acf9-3213-baf7-bddf2221ef16.gif[/img]

[color=#FF0000] [b][size=medium]● 字符类[/size][/b][/color]
[table]
| [color=#345286][b]符号[/b][/color] | [color=#345286][b]描述[/b][/color] |
| [color=#345286][b].[/b][/color] | [color=#345286]表示任何字符[/color] |
| [color=#345286][b][abc][/b][/color] | [color=#345286]包含 a, b, c 的任何字符[/color] |
| [color=#345286][b][^abc][/b][/color] | [color=#345286]除了 a, b, c 之外的任何字符(否定)[/color] |
| [color=#345286][b][a-zA-Z][/b][/color] | [color=#345286]任何 a 到 z 或 A 到 Z 的任何字符(范围)[/color] |
| [color=#345286][b][abc[hij]][/b][/color] | [color=#345286]任何 a, b, c, h, i, j 字符(合并)[/color] |
| [color=#345286][b][abc&&[hij]] [/b][/color] | [color=#345286]任何 h, i 或 j{交)[/color] |
| [color=#345286][b]\s[/b][/color] | [color=#345286]whitespace 符(空格[13], tab[\t], 换行[\r], 换页[\f], 回车[\n]) [/color] |
| [color=#345286][b]\S[/b][/color] | [color=#345286]非 whitespace 符([^\s])[/color] |
| [color=#345286][b]\d[/b][/color] | [color=#345286]数字 [0-9])[/color] |
| [color=#345286][b]\D[/b][/color] | [color=#345286]非数字 [^0-9])[/color] |
| [color=#345286][b]\w[/b][/color] | [color=#345286]word character [a-zA-Z_0-9])[/color] |
| [color=#345286][b]\W[/b][/color] | [color=#345286]非 word character [^\w])[/color] |
[/table]

[color=#FF0000] [b][size=medium]● 边界匹配[/size][/b][/color]
[table]
| [color=#345286][b]符号 [/b][/color] | [color=#345286][b]描述 [/b][/color] |
| [color=#345286][b]^[/b][/color] | [color=#345286]一行的开始[/color] |
| [color=#345286][b]$[/b][/color] | [color=#345286]一行的结束[/color] |
| [color=#345286][b]\b[/b][/color] | [color=#345286]词界[/color] |
| [color=#345286][b]\B[/b][/color] | [color=#345286]非词界[/color] |
| [color=#345286][b]\G[/b][/color] | [color=#345286]上一级结尾[/color] |
[/table]

[color=#FF0000] [b][size=medium]● 模式标记[/size][/b][/color]
[table]
| [color=#345286][b]Compile Flag(for Java)[/b][/color] | [color=#345286][b]Effect[/b][/color] |
| [color=#345286][b]Pattern.CANON_EQ[/b][/color] | [color=#345286]两个字符当且仅当它们的完全规范分解想匹配时,就认为它们是匹配的。例如,如果我们指定这个标记,表达式 "a\u030A" 就会匹配字符串 "?"。缺省情况下,匹配不考虑规范的等价性。[/color] |
| [color=#345286][b]Pattern.CASE_INSENSITIVE(?i)[/b][/color] | [color=#345286]缺省情况下,大小写不敏感的匹配假定只有在 US-ASCII 字符集中的字符才能进行。这个标记允许我们的模式匹配不考虑大小写(大些或小写)。通过指定 UNICODE_CASE 标记并与此标记结合起来,基于 Unicode 大小写不敏感的匹配就可以使能。[/color] |
| [color=#345286][b]Pattern.COMMENTS(?x)[/b][/color] | [color=#345286]在这种模式下,空格符将被忽略掉,并且以 # 开始直到行末的注释也会被忽略掉。通过嵌入的标记表达式也可以使能 Unix 的行模式。[/color] |
| [color=#345286][b]Pattern.DOTALL(?s)[/b][/color] | [color=#345286]在 dotall 模式下,表达式 "." 匹配所有字符,包括行终结符。缺省情况下,"." 表达式不匹配行终结符。[/color] |
| [color=#345286][b]Pattern.MULTILINE(?m)[/b][/color] | [color=#345286]在多行模式下,表达式 "^" 和 "$" 分别匹配一行的开始和结束。"^" 还匹配输入字符串的开始,而 "$" 也还匹配输入字符串的结尾。缺省情况下,这些表达式仅匹配输入的完整字符串的开始和结束。[/color] |
| [color=#345286][b]Pattern.UNICODE_CASE(?u)[/b][/color] | [color=#345286]当指定这个标记,并且使能 CASE_INSENSITIVE 时,大小写不敏感的匹配将按照与 Unicode 标准相一致的方式进行。缺省情况下,大小写不敏感的匹配假定只能在 US-ASCII 字符集中的字符才能匹配。[/color] |
| [color=#345286][b]Pattern.UNIX_LINE(?d)[/b][/color] | [color=#345286]在这种情况下,".", "^" 和 "$" 行为中,只识别行终结符 "\n"。[/color] |
[/table]
[table]
| [color=#345286][b]Compile Flag(for Javascript)[/b][/color] | [color=#345286][b]Effect[/b][/color] |
| [color=#345286][b]i[/b][/color] | [color=#345286]Perform case-insensitive matching.[/color] |
| [color=#345286][b]g[/b][/color] | [color=#345286]Perform a global matchthat is, find all matches rather than stopping after the first match.[/color] |
| [color=#345286][b]m[/b][/color] | [color=#345286]Multiline mode. ^ matches beginning of line or beginning of string, and $ matches end of line or end of string.[/color] |
[/table]

[color=#FF0000] [b][size=medium]● 例子[/size][/b][/color]

/**
* 此例子介绍 java 和 javascript 中有关正则表达式的基本 api
*/

//~--------------------------------------- For Java -------------------------------------
// 通过 Pattern 类的静态方法 compile,传递一正则表达式字符串,构建一 Pattern 对象
Pattern p = Pattern.compile(String regex);
// 使用 Pattern 对象去匹配一个CharSequence对象(String,StringBuffer和CharBuffer对象实现了此接口)
Matcher m = p.matcher(CharSequence input);
/**
* 接着可以使用 Matcher 对象访问结果,并利用一些方法来判断各种类型的匹配是成功或是失败:
* boolean matches();
* boolean lookingAt();
* boolean find();
* boolean find(int start);
*
* 如果模式匹配整个输入字符串,则 matches() 方法成功,否则失败。而且,如果输入字符串从一开始就是
* 模式的一个匹配,那么 lookingAt() 方法也是成功的。lookingAt() 不需要匹配整个输入,只要从一开始
* 能匹配即可,而 matches() 需要匹配整个输入。
*
* Pattern 对象还有一个静态方法:boolean matches(regex, input) 用于快速匹配(完全匹配)。
* 这和 boolean str.matches(regex) 是等价的。
*
* 组是由圆括号分开的正则表达式,随后可以根据它们的组号进行调用。第 0 组表示整个匹配表达式,第 1 组
* 表示第 1 个用圆括号括起来的组,等到。因此,在表达式 A(B(C))D 中,有三个组:第 0 组 ABCD,第 1 组
* 是 BC 以及第 2 组 C。
*
* Matcher 对象有一些方法可以向我们提供有关组的信息:
* public int groupCount() 返回本匹配器的模式中分组的数目。第 0 组不包括在内
* pubilc String group() 返回前一次匹配操作(例如:find()) 的第 0 组(整个匹配)
* public String group(int i) 返回在前一次匹配操作期间指定的组。如果匹配成功,但是指定的组没有
* 匹配输入字符串的任何部分,则返回 null
*
* find() 常用方式是:
* while (m.find()) {
* System.out.println(m.group());
* }
*
* while (m.find()) {
* for (int j = 0; j < m.groupCount(); j++) {
* System.out.println(m.group[j]);
* }
* System.out.println();
* }
*/

//~------------------------------------- For Javascript ---------------------------------
/**
* In JavaScript, regular expressions are represented by RegExp objects. RegExp objects
* may be created with the RegExp( ) constructor, of course, but they are more often
* created using a special literal syntax. Just as string literals are specified as
* characters within quotation marks, regular expression literals are specified as
* characters within a pair of slash (/) characters. Thus, your JavaScript code may
* contain lines like this:
*
* var pattern = /s$/;
*
* This line creates a new RegExp object and assigns it to the variable pattern. This
* particular RegExp object matches any string that ends with the letter "s." (I'll get
* into the grammar for defining patterns shortly.) This regular expression could have
* equivalently been defined with the RegExp( ) constructor like this:
*
* var pattern = new RegExp("s$");
*
* Strings support four methods that use regular expressions. They are search(),
* replace(), match() and split().
*
* The simplest is search( ). Function signature as follows:
* int search(Regex regex)
* int search(String literal)
* This method takes a regular expression argument and returns either the character
* position of the start of the first matching substring or -1 if there is no match.
* For example, the following call returns 4: "JavaScript".search(/script/i);
*
* If the argument to search( ) is not a regular expression, it is first converted to one
* by passing it to the RegExp constructor. search( ) does not support global searches; it
* ignores the g flag of its regular expression argument.
*
* Then is the replace( ) method. Function signature as follows:
* String replace(Regex regex, String replacement)
* String replace(String literal, String replacement)
* The replace( ) method performs a search-and-replace operation. It takes a regular
* expression as its first argument and a replacement string as its second argument. It
* searches the string on which it is called for matches with the specified pattern. If
* the regular expression has the g flag set, the replace( ) method replaces all matches
* in the string with the replacement string; otherwise, it replaces only the first match
* it finds. If the first argument to replace( ) is a string rather than a regular
* expression, the method searches for that string literally rather than converting it to
* a regular expression with the RegExp( ) constructor, as search( ) does.
*
* Next is the match( ) method. Function signature as follows:
* Array<String> match(Regex regex)
* The match( ) method is the most general of the String regular-expression methods. It
* takes a regular expression as its only argument (or converts its argument to a regular
* expression by passing it to the RegExp( ) constructor) and returns an array that
* contains the results of the match. If the regular expression has the g flag set, the
* method returns an array of all matches that appear in the string. For example:
*
* "1 plus 2 equals 3".match(/java/) // returns null
* "1 plus 2 equals 3".match(/\d+/) // returns ["1"]
* "1 plus 2 equals 3".match(/\d+/g) // returns ["1", "2", "3"]
*
* Finally, you should know about one more feature of the match( ) method. The array it
* returns has a length property, as all arrays do. When match( ) is invoked on a nonglo-
* bal regular expression, however, the returned array also has two other properties: the
* index property, which contains the character position within the string at which the
* match begins, and the input property, which is a copy of the target string. For a
* regular expression r and string s that does not have the g flag set, calling s.match(r)
* returns the same value as r.exec(s). The RegExp.exec( ) method is discussed a little
* later.
*
* The last of the regular-expression methods of the String object is split( ).
*
* The RegExp Object:
* Regular expressions are represented as RegExp objects. In addition to the RegExp( )
* constructor, RegExp objects support three methods and a number of properties. An unus-
* ual feature of the RegExp class is that it defines both class (or static) properties
* and instance properties. That is, it defines global properties that belong to the
* RegExp( ) constructor as well as other properties that belong to individual RegExp
* objects. The RegExp( ) constructor takes one or two string arguments and creates a new
* RegExp object. The first argument to this constructor is a string that contains the
* body of the regular expressionthe text that would appear within slashes in a regular
* expression literal. Note that both string literals and regular expressions use the \
* character for escape sequences, so when you pass a regular expression to RegExp( ) as a
* string literal, you must replace each \ character with \\. The second argument to
* RegExp( ) is optional. If supplied, it indicates the regular expression flags. It
* should be g, i, m, or a combination of those letters. For example:
*
* // Find all five-digit numbers in a string. Note the double \\ in this case.
* var zipcode = new RegExp("\\d{5}", "g");
*
* RegExp Methods for Pattern Matching:
* RegExp objects define two methods[exec( ) and test( )] that perform pattern matching
* operations; they behave similarly to the String methods described earlier. The main
* RegExp pattern matching method is exec( ). It is similar to the String match( ) method
* except that it is a RegExp method that takes a string, rather than a String method that
* takes a RegExp. The exec( ) method executes a regular expression on the specified
* string. That is, it searches the string for a match. If it finds none, it returns null.
* If it does find one, however, it returns an array just like the array returned by the
* match( ) method for nonglobal searches. Element 0 of the array contains the string that
* matched the regular expression, and any subsequent array elements contain the substrings
* that matched any parenthesized subexpressions. Furthermore, the index property contains
* the character position at which he match occurred, and the input property refers to the
* string that was searched.
*
* Unlike the match( ) method, exec( ) returns the same kind of array whether or not the
* regular expression has the global g flag. Recall that match( ) returns an array of
* matches when passed a global regular expression. exec( ), by contrast, always returns
* a single match and provides complete information about that match. When exec( ) is
* called on a regular expression that has the g flag, it sets the lastIndex property of
* the regular expression object to the character position immediately following the
* matched substring. If exec( ) is called on a regular expression that doesn't have g
* flag, it wouldn't change the lastIndex property, so when use while( ) to loop, you
* would get a dead loop when it could match the input(can't match wouldn't lead dead lo-
* op, for the loop condition is false). When exec( ) is invoked a second time for the
* same regular expression, it begins its search at the character position indicated by
* the lastIndex property. If exec( ) does not find a match, it resets lastIndex to 0.
* (You can also set lastIndex to 0 at any time, which you should do whenever you quit a
* search before you find the last match in one string and begin searching another string
* with the same RegExp object.) This special behavior allows you to call exec( ) repeate-
* dly in order to loop through all the regular expression matches in a string.
* For example:
*
* Regex Object's function signature as
* Array exec(String) (with g flag or not, it return array of size 1 unless it mathes none)
* boolean test(String) (it returns true if mathes, otherwise false)
*
* var content = "JavaScript is more fun than Java!";
* var p1 = /java/i;
* var p2 = /java/ig;
* var p3 = /javaX/i;

* p1.exec(content); // ["Java"], it doesn't change pattern's lastIndex property.
* p2.exec(content); // ["Java"] not ["Java", "Java"], it changes pattern's
* // lastIndex property for pattern with "g" flag
* p3.exec(content); // null
*
* var pattern = /Java/g;
* var text = "JavaScript is more fun than Java!";
* var result;
* while ((result = pattern.exec(text)) != null) {
* alert("Matched '" + result[0] + "'" +
* " at position " + result.index +
* "; next search begins at " + pattern.lastIndex);
* }
*
* The other RegExp method is test( ). test( ) is a much simpler method than exec( ). It
* takes a string and returns true if the string contains a match for the regular
* expression:
*
* var pattern = /java/i; // As long as the matched string occurs "java" ignoring
* pattern.test("JavaScript"); // case, which would return true for it is not the whole
* // match. If we need the whole match effect, we should
* // alter the pattern with prefix "^" and suffix "$".
*
* var pattern = /^java$/i; // The String must be "java" ignoring case and the matched
* pattern.test("JavaScript"); // string can't occur any other characters, which will
// return false for it is the whole match just as
* // java.util.regex.Pattern.matches().
*
* (/^java$/i).test("Javascript") is also legal javascript expression.
*
* var strRegex = "/java/i";
* (eval(strRegex)).test("hello java!"); // return true, it is legal javascript expression.
*
* Calling test( ) is equivalent to calling exec( ) and returning TRUE if the return value
* of exec( ) is not null. Because of this equivalence, the test( ) method behaves the
* same way as the exec( ) method when invoked for a global regular expression: it begins
* searching the specified string at the position specified by lastIndex, and if it finds
* a match, it sets lastIndex to the position of the character immediately following the
* match. Thus, you can loop through a string using the test( ) method just as you can
* with the exec( ) method.
*
* RegExp Instance Properties:
* Each RegExp object has five properties. The source property is a read-only string that
* contains the text of the regular expression. The global property is a read-only boolean
* value that specifies whether the regular expression has the g flag. The ignoreCase
* property is a read-only boolean value that specifies whether the regular expression has
* the i flag. The multiline property is a read-only boolean value that specifies whether
* the regular expression has the m flag. The final property is lastIndex, a read/write
* integer. For patterns with the g flag, this property stores the position in the string
* at which the next search is to begin. It is used by the exec( ) and test( ) methods.
*/



/**
* 使用 java 和 javascript 演示模式匹配。
* 这里使用多行和不区分大小的模式来演示,因为 java 和 javascript 中均有此模式。
*/

//~--------------------------------------- For Java -------------------------------------
// 1. Use pattern in literal regular expression(?mi)
Pattern pattern = Pattern.compile("(?mi)JAVA\\w*");
String input = "java has regex\nJava has regex\n" +
"JAVA has pretty good regular expressions\n" +
"Regular expressions are in Java";
// true
System.out.println(pattern.matcher(input).find());

// 2. Use Pattern.complile()'s reload method, use OR(|) operator to realize multi patterns
Pattern pattern = Pattern.compile("JAVA\\w*", Pattern.MULTILINE|Pattern.CASE_INSENSITIVE);
String s = "java has regex\nJava has regex\n" +
"JAVA has pretty good regular expressions\n" +
"Regular expressions are in Java";
// true
System.out.println(pattern.matcher(s).find());

//~------------------------------------- For Javascript ---------------------------------
// 1. Use pattern in literal regular expression(?mi)
var pattern = /JAVA/mi;
var input = "java has regex\nJava has regex\n" +
"JAVA has pretty good regular expressions\n" +
"Regular expressions are in Java";
// true
alert(pattern.test(input));

// 2. Use Regex object's two-argument constructor. It should be g, i, m, or a
// combination of those letters
/**
* If exec( ) is called on a regular expression that doesn't have g flag, it wouldn't
* change the lastIndex property, so when use while( ) to loop, you would get a dead loop
* when it could match the input(can't match wouldn't lead dead loop, for the loop
* condition is false).
*/
var pattern = new RegExp("JAVA", "gmi");
var input = "java has regex\nJava has regex\n" +
"JAVA has pretty good regular expressions\n" +
"Regular expressions are in Java";
// true
alert(pattern.test(input));



/**
* 分组替换
* 将字符串 "Aliy: 1983-09-21; Blaine: 2000-02-28; Tony: 1976-11-08"
* 提取出每个人生日的月份,并将月份替换成中文的月份
*/

//~--------------------------------------- For Java -------------------------------------
/**
* Matcher 对象有一些替换操作,对于我们能方便的处理文本的替换。
* replaceFirst(String replacement): 用 replacement 替换输入字符串中最先匹配的那部分
* replaceAll(String replacement): 用 replacement 替换输入字符串中所有的匹配部分
* appendReplacement(StringBuffer sbuf, String replacement): 逐步地在 subf 中执行替换,而不
* 是像 replaceFirst() 那样仅替换第一个匹配或者像 replaceAll() 是替换所有的匹配。这是个
* 非常重要的方法,因为它允许我们通过调用某些方法并执行一些其它处理来产生 replacement(而
* replaceFirst() 和 replaceAll() 只能输入固定字符串)。有了这个方法,我们就可以通过编程来
* 实现将目标拆分成组以及创建功能强大的替换。
* appendTail(StringBuffer subf): 在一个或多个 appendReplacement() 调用之后被调用,以便复制输
* 入字符串的剩余部分。
*
* replaceFirst() 和 replaceAll() 中的替换字符串仅是字面意义上的,如果我们想在每个替换上执行一些
* 操作,它们就不会太有帮助(replaceFirst 和 replaceAll() 在得到 Matcher 对象后可直接被调用,因此
* 它如果要处理分组情况时就无能为力(得到 Matcher 对象后调用,此时拿不到分组信息)。当然,这两个方
* 法可以在 while(Matcher.find()) 的循环中调用,循环中是可以得到分组信息的)。如果确实要那样的话,
* 我们需要使用 appendReplacement(),它可以让我们将任意数量的代码编写进执行替换的过程。通常地,
* 我们会逐步地执行所有替换,然后调用 appendTail(),但是如果我们想模仿 replaceFirst()(或者 "替换
* 第 n 个"),我们仅需要执行一次替换,然后调用 appendTail() 把剩余部分输入到 subf 中即可。
* 在 while(Matcher.find()) 中,我们可以通过 "$g"(其中 "g" 是组号)的形式,来在替代字符串中直接引用
* 被捕获的组。
*/
String[] months = new String[] {
"一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月",
"九月", "十月", "十一月", "十二月"
};
Pattern pattern = Pattern.compile("(\\w+)(\\s*:\\s*\\d{4}-)(\\d{2})(-\\d{2})");
String input = "以下是各个人员的出生日期信息:Aliy: 1983-09-21; Blaine: 2000-02-28; Tony:" +
" 1976-11-08; Joson: 1998-07-08; Kite: 2001-10-20; Sanfansicico: 1991-03-31。";
String result = null;
StringBuffer resultBuffer = new StringBuffer();
Matcher matcher = pattern.matcher(input);
// matcher.replaceFirst 和 matcher.replaceAll() 可以在此调用,但它们拿不到分组信息,除非将此方法
// 的调用写入 while() 循环中
while (matcher.find()) {
// 在循环中可以得到分组信息
String name = matcher.group(1);
String other1 = matcher.group(2);
String month = matcher.group(3);
String other2 = matcher.group(4);
matcher.appendReplacement(resultBuffer, "$1$2" +
months[Integer.parseInt(month)] + "$4");
}
// 把剩余部分输入到 resultBuffer 中
matcher.appendTail(resultBuffer);
// 结果:以下是各个人员的出生日期信息:Aliy: 1983-十月-21; Blaine: 2000-三月-28; Tony:
// 1976-十二月-08; Joson: 1998-八月-08; Kite: 2001-十一月-20; Sanfansicico: 1991-四月-31。
System.out.println(resultBuffer);

//~------------------------------------- For Javascript ---------------------------------
<script type="text/javascript" language="javascript">
var months = [
"一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月",
"九月", "十月", "十一月", "十二月" ];

var pattern = /(\w+\s*:\s*\d{4}-)(\d{2})(-\d{2})/g;
var input = "以下是各个人员的出生日期信息:Aliy: 1983-09-21; Blaine: 2000-02-28; Tony:" +
" 1976-11-08; Joson: 1998-07-08; Kite: 2001-10-20; Sanfansicico: 1991-03-31。";
/**
* 分成 3 组,$1 是第 1 组所代表的值,要用引号起来,$3 是第 3 组所代表的值,$2 是第 2 组所代表
* 的值,也就是月份,但 months["$2"] 会让 js 引擎去找 months 的 $2 的属性,因此我们对月份无能
* 为力了。如果不为分组信息做特殊处理,我们可以使用 String.replace(Regex, String) 来处理。
*/
var result = input.replace(pattern, "$1" + months["$2"] + "$3");
</script>

// 将 replace 函数的第二个参数使用 Function 对象
<script type="text/javascript" language="javascript">
/**
* replace 把若干个参数传入此函数中:
* 0: 依据正则表达式和源串得到的匹配的字符串,如:Aliy: 1983-09-21
* 1: 正则表达式匹配到的字符串的第 1 个分组值,如:Aliy: 1983-
* 2: 正则表达式匹配到的字符串的第 2 个分组值,如:Aliy: 09
* 3: 正则表达式匹配到的字符串的第 3 个分组值,如:-21
* ...
* n: 正则表达式匹配到的字符串的第 n 个分组值
* n + 1: 被匹配到的字符串在源串中的索引
* n + 2: 源串,如:以下是各个人员的出生日期信息:Aliy: 1983-09-21; Bla ...(省略)
*/
function replaceAction() {
for (var i = 0; i < arguments.length; i++) {
alert("第 " + i + " 个参数的值为: " + arguments[i]);
}

// 这里可以对匹配的字符串为所欲为了
// 这里直接返回匹配到的字符串,即不做任何替换
return arguments[0];
};

var months = [
"一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月",
"九月", "十月", "十一月", "十二月" ];

var pattern = /(\w+\s*:\s*\d{4}-)(\d{2})(-\d{2})/g;
var input = "以下是各个人员的出生日期信息:Aliy: 1983-09-21; Blaine: 2000-02-28; Tony:" +
" 1976-11-08; Joson: 1998-07-08; Kite: 2001-10-20; Sanfansicico: 1991-03-31。";
// js 引擎会将一些参数传入到 replaceAction 函数中,匹配 pattern 的串将被替换成 replaceAction
// 函数的返回值。详见 replaceAction 说明
var result = input.replace(pattern, replaceAction);
</script>

// 完整的代码
<script type="text/javascript" language="javascript">
function replaceAction() {
return arguments[1] + months[parseInt(arguments[2])] + arguments[3];
};

var months = [
"一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月",
"九月", "十月", "十一月", "十二月" ];

var pattern = /(\w+\s*:\s*\d{4}-)(\d{2})(-\d{2})/g;
var input = "以下是各个人员的出生日期信息:Aliy: 1983-09-21; Blaine: 2000-02-28; Tony:" +
" 1976-11-08; Joson: 1998-07-08; Kite: 2001-10-20; Sanfansicico: 1991-03-31。";
var result = input.replace(pattern, replaceAction);
// 结果:以下是各个人员的出生日期信息:Aliy: 1983-十月-21; Blaine: 2000-三月-28; Tony: 1976
// -十二月-08; Joson: 1998-八月-08; Kite: 2001-十一月-20; Sanfansicico: 1991-四月-31。
document.write(result);
</script>



/**
* 这里在再举一个关于分组替换的例子:
* 将 [长江实业(00001)] 这样的字符串替换成 ->
* [<a href="http://org.zachary.com/stock.action?stockcode=00001">长江实业</a>(00001)]
* 同时,做如下操作:
* 1. 将多个空格替换成一个空格( )
* 2. 将每行的前几个空格替换成四个空格( ),行首替换成 "<p>"
* 3. 将多个换行符替换成一个换行符,行尾替换成 "</p>"
*/

/**
* 编写正则表达式的思路:
* 1. 出现 "["
* 2. 里面只能出现非 "[" 和 "]" 任何字符(多个)
* 3. 紧接着是括号里出现 1 到 6 位的数字
* 4. 最后一个字符必须是 "]"
*/

// java 的正则表达
String regExp = "\\[([^\\[^\\]]+)\\((\\d{1,6})\\)\\]";
// javascript 的正则表达式
var regex = new RegExp("\\[([^\\[^\\]]+)\\((\\d{1,6})\\)\\]", "g");
var pattern = /\[([^\[^\]]+)\((\d{1,6})\)/g;

//~--------------------------------------- For Java -------------------------------------
Pattern pattern = Pattern.compile("\\[([^\\[^\\]]+)\\((\\d{1,6})\\)\\]");
String input = "北京市委常委会昨天召开会议会议 强调,要加大投资力度,加快基础设施\r\n\r\n\r\n" +
" 建设步伐,保持经济平稳较快发展。会议部署,今后两年共安排政府投资1200亿\r\n\r\n" +
" 至1500,预计带动社会投资10000亿元[长江实业(00001)]天津滨海区新增337个储备项目投资\r\n" +
" 规模达3000亿[香港中华煤气(00003)]广东预计明年可完成1.3万亿元投资\r\n\r\n" +
" [中国自动化集团(00569)]浙江拟利用3000亿拉动内需帮扶[东方报业集团(00018)]\r\n\r\n" +
"[东方报业集团(00018)]中小企业。";
Matcher matcher = pattern.matcher(input);
String result = matcher.replaceAll("[<a href=\"http://org.zachary.com/stock.action?stockcode=$2\">$1</a>($2)]");

// 将两个或两个以上的空格替换成一个空格( )
result = result.replaceAll("(\40){2,}", " ");
// 将多个换行符替换成一个换行符
result = result.replaceAll("(\r\n)+", "\r\n");
result = result.replaceAll("(?m)^\40*", "<p>    ");
// (?m) 相当于 Pattern.MULTILINE 模式标记,"^" 和 "$" 分别匹配一行的开始和结束。将行尾替换成 "</p>"
result = result.replaceAll("(?m)$", "</p>");

System.out.println(result);

//~------------------------------------- For Javascript ---------------------------------
<script type="text/javascript" language="javascript">
var input = "北京市委常委会昨天召开会议会议 强调,要加大投资力度,加快基础设施\r\n\r\n\r\n" +
" 建设步伐,保持经济平稳较快发展。会议部署,今后两年共安排政府投资1200亿\r\n\r\n" +
" 至1500,预计带动社会投资10000亿元[长江实业(00001)]天津滨海区新增337个储备项目投资\r\n" +
" 规模达3000亿[香港中华煤气(00003)]广东预计明年可完成1.3万亿元投资\r\n\r\n" +
" [中国自动化集团(00569)]浙江拟利用3000亿拉动内需帮扶[东方报业集团(00018)]\r\n\r\n" +
"[东方报业集团(00018)]中小企业。";

var re2 -> nullgex = new RegExp("\\[([^\\[^\\]]+)\\((\\d{1,6})\\)\\]", "g");
var pattern = /\[([^\[^\]]+)\((\d{1,6})\)/g;

var result = input.replace(regex,
"[<a href=\"http://org.zachary.com/stock.action?stockcode=$2\">$1</a>($2)]");

// 将两个或两个以上的空格替换成一个空格( )
result = result.replace(/\40{2,}/g, " ");
// 将多个换行符替换成一个换行符
result = result.replace(/(\r\n)+/g, "\r\n");
result = result.replace("/^40*/gm", "<p>    ");
// 将行尾替换成 "</p>"
result = result.replace(/$/gm, "</p>");

document.write(result);
</script>



/**
* 正则表达式中并不提供关于数学的任何功能,所以只能使用冗长的分组来表达 ip 地址。
*
* 假设有一个数,要么是 250 - 255 或 10 - 99,来分析以下几种正则表达式的匹配结果:
* ① 只要 input 出现 25[0-5] 或 [1-9]\d 就匹配,而不管 input 中出现了并不匹配的字符。
* var regex = /25[0-5]|[1-9]\d/;
× 23 -> 23 [1-9]\d
* 235 -> 23 [1-9]\d
* 590 -> 59 [1-9]\d
* 254 -> 254 25[0-5]
* 252X -> 252 25[0-5]
* 2 -> null
* K235K -> 23 [1-9]\d
* AB25378XY -> 253 25[0-5]
*
* ② 第 ① 种表达式被证明是失败的,由于 javascript 中没有 java 中的完全匹配的概念,因此我们采用
* 字符前 ^ 和字符后 $ 来达到这种全部匹配的效果。
* var regex = /^25[0-5]|[1-9]\d$/;
* 255 -> 255 ^25[0-5]
* 2556 -> 255 ^25[0-5]
* 25589 -> 255 ^25[0-5]
* 253X -> 253
* 576576 -> 76 [1-9]\d$
* 576576HW -> null
* 通过分析,也没有达到预期的效果,其实上面的正则表达式所表达的含义是:要么以 25[0-5] 开头,
* 要么以 [1-9]\d 结束。因此 576576 可以匹配到 78,因为它满足了 [1-9]\d$。这里的活运算符("|"),是
* ^25[o-5] 整体和 [1-9]\d$ 整体进行或运算,而不是 [0-5] 和 [1-9] 进行或运算,关于 [0-5] 和
× [1-9] 进行或运算的例子,参考 ④。
*
* ③ 这个时候,我们需要将 25[0-5]|[1-9]\d 用括弧给括起来,使其作为一个整体,并加 ^ 和 $,这样
* 就达到了我们所需要的效果。
* var regex = /^(25[0-5]|[1-9]\d)$/;
* 23 -> 23, 23 [1-9]\d
* 235 -> null
* 590 -> null
* 254 -> 254, 254 25[0-5]
* 252X -> null
* 5 -> null
* 88 -> 88, 88 [1-9]\d
* 066 -> null
* 这里匹配的结果只所以出现两次,是因为加了括弧,它同时是分组的含义,第一个匹配的是匹配的完整结
* 果,第二个匹配的是第一个分组的值。
*
* ④ 对 ② 的补充说明,[0-5] 和 [1-9] 进行或运算。
* var regex = /25([0-5]|[1-9])\d/;
* 253 -> null
* 2532 -> 2532, 3
* 2578A -> 2578, 7
* XY2580UK -> 2580, 8
* 之所以 XY2580UK 也可以被匹配,原因同 ①,这不是完全匹配的正则表达式。
*
* ip 地址的每段只能从 0 到 255,数字前不能有 0,分解这些数字如下:
* ① 0 - 9 -> \d
* ② 10 - 99 -> [1-9]\d
* ③ 100 - 199 -> 1\d{2}
* ④ 200 - 249 -> 2[0-4]\d
* ⑤ 250 - 255 -> 25[0-5]
*
× 以下是两种情况的正则表达式以匹配 ip 地址:
* ① (\d{1,3}.){3}(\d{1,3}) (\d{1,3}.){3} 再连最后一个数字:
* /^((25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/;
* ② (\d{1,3})(.\d{1,3}){3} 起始一个数字再连 (.\d{1,3}){3}:
* /^(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)(\.(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)){3}$/;
*
* 这个正则表达式写的复杂了些,它表明每段的数字的开头不能是 0。如果开头可以为 0,表达式要稍微简洁些。
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值