转自CSDN,5年前写的第一篇技术类的文章,当时还是个小菜鸟,一转眼5年过去了
https://blog.csdn.net/webgeek/article/details/8617138
----------------------------------------------------------------------说正事分割线------------------------------------------------------------------------------------
javascript 正则表达式-贪婪模式与懒惰模式
前段时间对自己以前了解的正则表达式的基础知识做了一个总结,有了基础知识已经能够能应付大多数情况,像字符串匹配、正则替换、校验等;但是再使用的过程中你可能发现一个问题,就是当使用重复元字符匹配数量时,总是会尽可能长的去匹配,而有时这恰恰不是你想要的。看下面的例子:
你希望匹配出每个链接的html内容。如果你用 会首先匹配到整个字符串,而不是你希望的谷歌和百度。
为什么会出现这种情况呢?这就是正则表达式的贪婪模式。当出现重复数量的时候,会尽可能的多匹配。上述的正则表达式中 . 表示任意字符,* 代表可以重复出现任意个,根据正则表达式的贪婪个性,不匹配到最后才怪呢。就像人一样,有贪婪就有懒惰,一个?就可以让正则立刻改变本性,这时的正则就表现懒惰模式的本性了。
修改后的正则表达式为:,这时就可以匹配出每个连接的html了。
测试用例为:
package org.buzheng.test;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Test {
public static void main(String[] args) throws Exception {
System.out.println("贪婪模式:");
Pattern pattern = Pattern.compile("");
Matcher matcher = pattern.matcher(value);
while (matcher.find()) {
System.out.println(matcher.group(0));
}
System.out.println("贪婪模式:");
pattern = Pattern.compile("");
matcher = pattern.matcher(value);
while (matcher.find()) {
System.out.println(matcher.group(0));
}
}
}
最后总结一下数量元字符懒惰模式的常用写法(其实就是多了一个?):
x*? 0个或者任意个x, 最少匹配
x?? 0个或这1个x, 最少匹配
x+? 至少1个x, 最少匹配
x{n,}? 至少n个x, 最少匹配
x{n,m}? 至少n个x,至多m个x, 最少匹配
--------------------------------------------------
javascript 正则表达式-零宽断言
http://buzheng.org/blog/regex-zero-width-assertion/
正则表达式里面比较高级的应用就属于零宽断言了。那么什么是零宽断言呢?拆分法从字面上分析一下,零宽,即宽带为0,意味者不会返回匹配的字符,以为匹配的是当前字符的位置。断言,就是预言、假设,意味着从此处假设存在什么情况。那么零宽断言的意思就是假定从此位置开始满足某种情况。
根据断言字符串位于当前位置的前后关系,分为正向和反向断言,根据断言肯定和否定的语气,又有正向否定断言和反向否定断言。肯定即断言存在该字符串、否定即相反的意思:存在的不是该字符串,总之概念比较绕口,下表介绍的时候顺便给出英文:
(?=X) 正向断言,假定该位置后跟的是X
zero-width positive lookahead
(?!X) 正向否定断言,假设该位置后跟的不是X
zero-width negative lookahead
(?<=X) 反向断言,假设该位置前跟的是X
zero-width positive lookbehind
(?
zero-width negative lookbehind
举例:
(?=X) 正向断言
[^s]+?(?=ing) 来匹配 having doing listing,会匹配出 hav, do, list,注意:并不会匹配出ing,因为ing是零宽断言的部分。
(?!X) 正向否定断言
一个用户注册功能的密码有如下要求:由数字和字母组成,并且要同时含有数字和字母,且长度要在8-16位之间。
如何分析需求?拆分!这就是软件设计的一般思路了。于是乎,拆分需求如下:
1,不能全部是数字
2,不能全部是字母
3,必须是数字或字母
只要能同时满足上面3个要求就可以了,写出来如下:
^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,16}$
分开来注释一下:
^ 匹配一行的开头位置
(?![0-9]+$) 预测该位置后面不全是数字
(?![a-zA-Z]+$) 预测该位置后面不全是字母
[0-9A-Za-z] {8,16} 由8-16位数字或这字母组成
$ 匹配行结尾位置
(?<=X) 反向断言
(?<=hell)[a-z]+ 来匹配test hellen hellas helloween,会匹配出 en, as, oween
(?
[a-z]+(?
注:所有的案例都在UE下进行测试。
----------------------------------------------------------------
javascript中的递归
递归做为一种算法在程序设计语言中广泛应用.是指函数/过程/子程序在运行过程中直接或间接调用自身而产生的重入现象.递归是计算机科学的一个重要概念,递归的方法是程序设计中有效的方法,采用递归编写程序能使程序变得简洁和清晰.。
递归函数:把直接或间接地调用自身的函数称为递归函数。函数的构建通常需要一个函数或者一个过程来完成。在数学上,关于递归函数的定义如下:对于某一函数f(x),其定义域是集合A,那么若对于A集合中的某一个值X0,其函数值f(x0)由f(f(x0))决定,那么就称f(x)为递归函数。
递归函数是在一个函数通过调用自身的情况下去解决的。方式如下:
JavaScript代码
function factorial(num)
{
if(num <= 1)
{
return 1;
}
else
{
return num*factorial(num-1);
}
}
但是这在js里面可能会出现错误:
JavaScript代码
var anotherFactorial = factorial;
factorial=null;
alert(anoterFactorial(4));
因为在调用anoterFactorial时内部的factorial已经不存在了。
解决方法是通过arguments.callee来解决。
如下:
JavaScript代码
function factorial(num)
{
if(num <= 1)
{
return 1;
}
else
{
return num*arguments.callee(num-1);
}
var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4));
}
如果在一个很复杂的程序中我们可能只需要调用一次该函数,为了函数的精简我们当然要努力较少函数名的定义,这是很自然会想到用匿名函数来直接执行。但是如果是匿名函数如何实现递归?arguments.callee正好派上用场,他指代的就是当前执行的函数的引用。
arguments.callee
在 javascript函数体内,标识符arguments具有特殊含义。它是调用对象的一个特殊属性,用来引用Arguments对象。 Arugments对象就像数组,注意这里只是像并不是哈。javascript函数体内,arguments像数组(并不是真的数组,是一个 Arguments对象,再次强调)一样,有length属性,可以代 表传给函数的参数的个数。
引用一个形式参数可以用参数名,也可以用arguments[]数组形式,其中arguments[0]表示第一个参数。所以,javascript中Arguments对象是函数的实际参数,下面,我们一起来进入这神奇的国度,一窥究竟。
arguments.length属性:js不会主动为你判断你到底给函数传了多少个参数,如果你多传了,多余的部分就没有被使用,如果你少传了,那么没传的参数值就是undefined
所以我们可以借助arguments的length属性来检测调用函数时是否使用了正确数目的实际参数,因为javascript是不会为你做这些事的。
JavaScript代码
function f(x,y,z)
{
//首先检查传递的参数数量是否正确
if(arguments.length != 3)
{
throw new Error("function f called with " + arguments.length + "arguments ,but it not 3 arguments.");
}
//下面运行真正的函数
}
arguments还为我们提供了这样一种可能,就是为一个函数传任意数目的实际参数:比如说,我想判断你传给我的一些数字的大小,取出最大的那个,对,没错,你传多少参数都行,但是前提是你要传数字,因为我在函数内部懒得判断了。
JavaScript代码
function max()
{
var m = Number.NEGATIVE_INFINITY;//Number.NEGATIVE_INFINITY JavaScript内最小的数字了
for(var i = 0; i < arguments.length; i++)
{
//只要有任何一个参数比m大,那么m就变成了这个参数的值
if(arguments[i] > m)
m = arguments[i];
}
return m;
}
怎么样?这个方法很巧妙吧?
说明一下arguments与真正传的形式参数是一致的:比如,你给函数传了一个叫param的参数,并且只有这一个参数,那么param与arguments[0]都是对这个参数值的引用,改变其中一个值,即改变了二者所有的值。
JavaScript代码
function change(param)
{
//比如我传的param为simaopig,那么alert就是simaopig,
//如果啥也没传就会alert undefined
alert(param);
//用arguments[0]改变了这个参数的值
arguments[0] = 'xiaoxiaozi';
//没错,这个值变成了xiaoxiaozi
alert(param);
}
arguments的callee属性:arguments的callee属性是用来引用当前正在执行的函数,这对未命名的函数调用自身非常有好处。
现在用arguments的这个callee简单的实现。
JavaScript代码
//用函数直接量,采用 arguments.callee属性实现递归函数
var result = function(x){
if(x<=1) return 1;
return x*arguments.callee(x-1);
};
在最后提醒大家一点,既然这个arguments这么厉害,那么我们就不要为变量命名为arguments 了,事实上arguments是javascript的保留字之一。
-----------------------------------------------------------
常用的JavaScript验证正则表达式
匹配中文字符的正则表达式: [u4e00-u9fa5]
评注:匹配中文还真是个头疼的事,有了这个表达式就好办了
匹配双字节字符(包括汉字在内):[^x00-xff]
评注:可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1)
匹配空白行的正则表达式:ns*r
评注:可以用来删除空白行
匹配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个英文字母或者下划线组成的字符串
在使用RegularExpressionValidator验证控件时的验证功能及其验证表达式介绍如下:
只能输入数字:“^[0-9]*$”
只能输入n位的数字:“^d{n}$”
只能输入至少n位数字:“^d{n,}$”
只能输入m-n位的数字:“^d{m,n}$”
只能输入零和非零开头的数字:“^(0|[1-9][0-9]*)$”
只能输入有两位小数的正实数:“^[0-9]+(.[0-9]{2})?$”
只能输入有1-3位小数的正实数:“^[0-9]+(.[0-9]{1,3})?$”
只能输入非零的正整数:“^+?[1-9][0-9]*$”
只能输入非零的负整数:“^-[1-9][0-9]*$”
只能输入长度为3的字符:“^.{3}$”
只能输入由26个英文字母组成的字符串:“^[A-Za-z]+$”
只能输入由26个大写英文字母组成的字符串:“^[A-Z]+$”
只能输入由26个小写英文字母组成的字符串:“^[a-z]+$”
只能输入由数字和26个英文字母组成的字符串:“^[A-Za-z0-9]+$”
只能输入由数字、26个英文字母或者下划线组成的字符串:“^w+$”
验证用户密码:“^[a-zA-Z]w{5,17}$”正确格式为:以字母开头,长度在6-18之间,
只能包含字符、数字和下划线。
验证是否含有^%&’,;=?$”等字符:“[^%&',;=?$x22]+”
只能输入汉字:“^[u4e00-u9fa5],{0,}$”
验证Email地址:“^w+[-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*$”
验证InternetURL:“^http://([w-]+.)+[w-]+(/[w-./?%&=]*)?$”
验证电话号码:“^((d{3,4})|d{3,4}-)?d{7,8}$”
正确格式为:“XXXX-XXXXXXX”,“XXXX-XXXXXXXX”,“XXX-XXXXXXX”,
“XXX-XXXXXXXX”,“XXXXXXX”,“XXXXXXXX”。
验证身份证号(15位或18位数字):“^d{15}|d{}18$”
验证一年的12个月:“^(0?[1-9]|1[0-2])$”正确格式为:“01”-“09”和“1”“12”
验证一个月的31天:“^((0?[1-9])|((1|2)[0-9])|30|31)$”
正确格式为:“01”“09”和“1”“31”。
匹配中文字符的正则表达式: [u4e00-u9fa5]
匹配双字节字符(包括汉字在内):[^x00-xff]
匹配空行的正则表达式:n[s| ]*r
匹配HTML标记的正则表达式:/< (.*)>.*|< (.*) />/
匹配首尾空格的正则表达式:(^s*)|(s*$)
匹配Email地址的正则表达式:w+([-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*
匹配网址URL的正则表达式:http://([w-]+.)+[w-]+(/[w- ./?%&=]*)?
(1)应用:计算字符串的长度(一个双字节字符长度计2,ASCII字符计1)
String.prototype.len=function(){return this.replace([^x00-xff]/g,”aa”).length;}
(2)应用:javascript中没有像vbscript那样的trim函数,我们就可以利用这个表达式来实现
String.prototype.trim = function()
{
return this.replace(/(^s*)|(s*$)/g, “”);
}
(3)应用:利用正则表达式分解和转换IP地址
function IP2V(ip) //IP地址转换成对应数值
{
re=/(d+).(d+).(d+).(d+)/g //匹配IP地址的正则表达式
if(re.test(ip))
{
return RegExp.$1*Math.pow(255,3))+RegExp.$2*Math.pow(255,2))+RegExp.$3*255+RegExp.$4*1
}
else
{
throw new Error(”Not a valid IP address!”)
}
}
(4)应用:从URL地址中提取文件名的javascript程序
s=”http://www.9499.net/page1.htm”;
s=s.replace(/(.*/){0,}([^.]+).*/ig,”$2″) ; //Page1.htm
(5)应用:利用正则表达式限制网页表单里的文本框输入内容
用 正则表达式限制只能输入中文:onkeyup=”value=”/blog/value.replace(/["^u4E00-u9FA5]/g,”) ” onbeforepaste=”clipboardData.setData(’text’,clipboardData.getData(’text’).replace(/[^u4E00-u9FA5]/g,”))”
用 正则表达式限制只能输入全角字符: onkeyup=”value=”/blog/value.replace(/["^uFF00-uFFFF]/g,”) ” onbeforepaste=”clipboardData.setData(’text’,clipboardData.getData(’text’).replace(/[^uFF00-uFFFF]/g,”))”
用 正则表达式限制只能输入数字:onkeyup=”value=”/blog/value.replace(/["^d]/g,”) “onbeforepaste= “clipboardData.setData(’text’,clipboardData.getData(’text’).replace(/[^d]/g,”))”
用 正则表达式限制只能输入数字和英文:onkeyup=”value=”/blog/value.replace(/[W]/g,””) “onbeforepaste=”clipboardData.setData(’text’,clipboardData.getData(’text’).replace(/[^d]/g,”