C# 正则表达式及常用正则表达式
元字符
描述
.点
匹配任何单个字符。例如正则表达式r.t匹配这些字符串:rat、rut、r t,但是不匹配root。
$
匹配行结束符。例如正则表达式weasel$ 能够匹配字符串"He's a weasel"的末尾 ,但是不能匹配字符串"They are a bunch of weasels."
^
匹配一行的开始。例如正则表达式^When in能够匹配字符串"When in the course of human events"的开始,但是不能匹配"What and When in the"
*
匹配0或多个正好在它之前的那个字符。例如正则表达式.*意味着能够匹配任意数量的任何字符。
\
这是引用符,用来将这里列出的这些元字符当作普通的字符来进行匹配。例如正则表达式\$被用来匹配美元符号,而不是行尾,类似的,正则表达式\.用来匹配点字符,而不是任何字符的通配符。
[ ]
[c1-c2]
[^c1-c2]
匹配括号中的任何一个字符。例如正则表达式r[aou]t匹配rat、rot和rut,但是不匹配ret。可以在括号中使用连字符-来指定字符的区间,例如正则表达式[0-9]可以匹配任何数字字符;还可以制定多个区间,例如正则表达式[A-Za-z]可以匹配任何大小写字母。另一个重要的用法是“排除”,要想匹配除了指定区间之外的字符——也就是所谓的补集——在左边的括号和第一个字符之间使用^字符,例如正则表达式[^269A-Z] 将匹配除了2、6、9和所有大写字母之外的任何字符。
\< \>
匹配词(word)的开始(\<)和结束(\>)。例如正则表达式\<the\>能够匹配字符串"for the wise"中的"the",但是不能匹配字符串"otherwise"中的"the"。注意:这个元字符不是所有的软件都支持的。
\( \)
将 \( 和 \) 之间的表达式定义为“组”(group),并且将匹配这个表达式的字符保存到一个临时区域(一个正则表达式中最多可以保存9个),它们可以用 \1 到\9 的符号来引用。
|
将两个匹配条件进行逻辑“或”(Or)运算。例如正则表达式(him|her) 匹配"it belongs to him"和"it belongs to her",但是不能匹配"it belongs to them."。注意:这个元字符不是所有的软件都支持的。
+
匹配1或多个正好在它之前的那个字符。例如正则表达式9+匹配9、99、999等。注意:这个元字符不是所有的软件都支持的。
?
匹配0或1个正好在它之前的那个字符。注意:这个元字符不是所有的软件都支持的。
\{i\}
\{i,j\}
匹配指定数目的字符,这些字符是在它之前的表达式定义的。例如正则表达式A[0-9]\{3\} 能够匹配字符"A"后面跟着正好3个数字字符的串,例如A123、A348等,但是不匹配A1234。而正则表达式[0-9]\{4,6\} 匹配连续的任意4个、5个或者6个数字字符。注意:这个元字符不是所有的软件都支持的。
/*******************匹配常见的格式*********************/
//var pattern=/^\w{5,10}$/; //最少5位的下划线、字母、数字,\w+相当于\w{1,} \w*相当于\w{0,} \w?相当于\w{0,1} \w{5,10}5到10位
//var pattern1=/^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$/;//15位的身份证
//var pattern2=/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{4}$/;//18位的身份证
//var pattern=/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;//匹配E-MALI地址
//var pattern=/^http:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/;//匹配网址
//var pattern=/http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/;//匹配网址
//var pattern=/^[\u4e00-\u9fa5]$/;//匹配中文字符(单个汉字)
//var pattern=/^[1-9]\d{5}(?!\d)$/;//匹配邮政编码
//var pattern=/^[1-2][0-9][0-9][0-9]-[0-1]{0,1}[0-9]-[0-3]{0,1}[0-9]$/;//匹配日期 如:1900-01-01
//var pattern=/^[^\x00-\xff]$/;//匹配双字节字符(包括汉字在内的单个字符)
//var pattern=/^<(.*)>.*<\/\1>|<(.*) \/>$/;//匹配HTML标记
//var pattern=/<(\S*?)[^>]*>.*?<\/\1>|<.*? \/>/;//匹配HTML标记
//var pattern=/^\n[\s| ]*\r$/;//可以用来删除空白行
//var pattern=/^(\s*)|(\s*)$///可以用来删除行首尾的空白字符(包括空格、制表符、换页符等等)
//var pattern=/^[a-zA-Z][a-zA-Z0-9_]{4,15}$/;//字母开头,限制5-16字节,允许字母数字下划线
//var pattern=/^\d{3}-\d{8}|\d{4}-\d{7,8}$/;//匹配国内电话 如:0739-8888888(8) 或 020-88888888
//var pattern=/^[1-9][0-9]{4,}$/;//匹配QQ号码 腾讯QQ号从10000开始
//var pattern=/^\d+\.\d+\.\d+\.\d+$/;//匹配IP地址
/*******************匹配特定数字*********************/
//var pattern=/^(\w)\1{4,}*$/;//匹配整数
//var pattern=/-?[1-9]\d*$/;//匹配整数
//var pattern=/^[1-9]\d*$/;//匹配正整数
//var pattern=/^-[1-9]\d*$/;//匹配负整数
//var pattern=/^[1-9]\d*|0$/;//匹配非负整数
//var pattern=/^-[1-9]\d*|0$/;//匹配非正整数
//var pattern=/^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$/;//匹配正浮点数
//var pattern=/^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$/;//匹配负浮点数
//var pattern=/^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$/;//匹配浮点数
//var pattern=/^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$/;//匹配非负浮点数
//var pattern=/^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$/;//匹配非正浮点数
/********************匹配特定字符串*****************/
//var pattern=/^[A-Za-z]+$/;//匹配由26个英文字母组成的字符串
//var pattern=/^[A-Z]+$/;//匹配由26个英文字母的大写组成的字符串
//var pattern=/^[a-z]+$/;//匹配由26个英文字母的小写组成的字符串
//var pattern=/^[A-Za-z0-9]+$/;//匹配由数字和26个英文字母组成的字符串
//var pattern=/^\w+$/;//匹配由数字、26个英文字母或者下划线组成的字符串
<script>
if(!pattern.test(document.getElementById("content").value)){
alert("请输入正确的格式!");
return false;
}
}
</script>
<%
‘限制只能输入中文:onkeyup="value=value.replace(/[^\u4E00-\u9FA5]/g,‘‘)" onbeforepaste="clipboardData.setData(‘text‘,clipboardData.getData(‘text‘).replace(/[^\u4E00-\u9FA5]/g,‘‘))"
‘限制只能输入全角字符:onkeyup="value=value.replace(/[^\uFF00-\uFFFF]/g,‘‘)" onbeforepaste="clipboardData.setData(‘text‘,clipboardData.getData(‘text‘).replace(/[^\uFF00-\uFFFF]/g,‘‘))"
‘限制只能输入数字:onkeyup="value=value.replace(/[^\d]/g,‘‘) "onbeforepaste="clipboardData.setData(‘text‘,clipboardData.getData(‘text‘).replace(/[^\d]/g,‘‘))"
‘限制只能输入数字和英文:onkeyup="value=value.replace(/[\W]/g,‘‘) "onbeforepaste="clipboardData.setData(‘text‘,clipboardData.getData(‘text‘).replace(/[^\d]/g,‘‘))"
%>
匹配模式+环视(顺序环视、逆序环视)+贪婪与非贪婪 RegexOptions.Multiline
“^”匹配结果分析
在不开启多行模式时,“^”只匹配字符串的开始位置,也就是位置0。
在开启了多行模式后,“^”匹配字符串开始位置和每个“\n”之后的行起始位置。 “$”匹配结果分析
在不开启多行模式时,如果字符结尾是“\n”,那么“$”会匹配结尾“\n”之前和结束两个位置。
在开启多行模式后,“$”匹配每行“\n”之前的位置和字符串结束位置。 需要注意的是,在.NET中,无论是否开启多行模式,“^”和“$”匹配的都只是一个位置,是零宽度的。其它语言中“^”和“$”的意义可能会有所不同。
只有在正则表达式中涉及到多行的“^”和“$”的匹配时,才使用Multiline模式。 RegexOptions.Compiled
Compiled改变的是.NET中正则表达式的编译方式。启用了Compiled模式,会延长启动时间,占用更多的内存,会提高匹配速度。当然,对最终性能的影响,需要根据具体问题综合考虑的。这一模式也是被“滥”用最多的模式之一。 程序运行过程中,第一次遇到正则表达式,需要加载正则引擎,对正则表达式进行必要的语法检查,并做适当的优化,最后把它转换为适合正则引擎应用的形式。这种“解析”过程,对于复杂的正则表达式,频繁调用或是匹配较大的数据源时,对效率的影响较大。 这时可以在构建正则表达式时开启Compiled模式。这样做会将正则表达式直接编译为MSIL代码,在正则匹配过程中,可以由JIT优化为更快的本地机器代码,获得更高的匹配速度。但这种方式会降低正则的解析速度,占用更多的内存,而且它占用的内存在程序运行过程中会一直占用,无法释放。 什么场景下使用Compiled模式,需要根据实际情况具体问题具体分析,一般来说,以下场景不适合使用Compiled模式:
.对匹配效率没有要求的场景;
.非常简单的正则表达式;
.极少调用的方法中声明的正则表达式;
.循环体中声明的正则表达式(除了动态生成的正则表达式,否则不要在循环体内声明正则表达式);
.静态方法中声明的正则表达式(静态方法每次调用都需要重新编辑正则表达式,使用Compiled模式只会降低效率)。 RegexOptions.RightToLeft
RightToLeft改变的是正则表达式匹配的顺序,从右到左进行匹配
一个由字母组成的字符串,最长14位,要求每隔2位加一个逗号,最左边不加,求一个好的算法
例:“abcdefg” 返回“a,bc,de,fg”
代码实现:
string test = "abcdefg";
string result = Regex.Replace(test, @"(?<!^)[a-zA-Z]{2}", ",$0", RegexOptions.RightToLeft); RegexOptions.ExplicitCapture
这一模式改变的是普通捕获组的匹配行为。将普通捕获组解释为非捕获组,只有显式命名的命名捕获组才当作捕获组使用。
捕获组的作用是将括号()内子表达式匹配到的内容保存到内存中一个组里,供以后引用,在.NET中捕获组有两种形式
(Expression) 普通捕获组
(?<name>Expression) 命名捕获组
其它形式的(?...)都不是捕获组。
但是(Expression)这种捕获组语法规则也带来一个副作用,在一些不得不使用()的场合,会默认为使用了捕获组,将匹配到的内容保存到内存中,而有些情况下这些内容并不需要关心的,浪费了系统资源,降低了匹配效率,所以才有了非捕获组(?:Expression)的出现,来抵消这一副作用。而非捕获组带来的另一个副作用的就是可读性的降低。
string test = "<li title=\"截至2009-07-28 20:45:49,用户的总技术分为:5988;截至2009-07-26日,用户的总技术分排名为:4133\">(...)</li>";
Regex reg = new Regex(@"([01][0-9]|2[0-3])(:[0-5][0-9]){2}", RegexOptions.ExplicitCapture);
MatchCollection mc = reg.Matches(test);
string s = "";
foreach (Match m in mc)
{
s += m.Value + "\n";
s += m.Groups[].Value + "\n";
s += m.Groups[].Value + "\n";
}
Console.Write(s);
未开启RegexOptions.ExplicitCapture
/*
20:45:49
20
:49
*/
开启的结果是
/*
20:45:49 */ (?imnsx-imnsx:)形式
string[] test = new string[] { "Abc", "AbcdefGHIjklmn", "abcdefghijklmn" };
Regex reg = new Regex(@"^[A-Z](?i:[A-Z]{9,19})$");
string str = "";
foreach (string s in test)
{
str += "源字符串:" + s.PadRight(, ' ') + " 匹配结果: " + reg.IsMatch(s) + "\n";
}
Console.WriteLine(str);
/*--------输出--------
源字符串: Abc 匹配结果: False
源字符串: AbcdefGHIjklmn 匹配结果: True
源字符串: abcdefghijklmn 匹配结果: False
*/
语法:(?-i:Expression)
这种语法规则表达为括号内的子表达式关闭忽略大小写模式。通常与全局匹配模式配合使用,表示全局为忽略大小写的,局部为严格区分大小写。
string test = "<DIV id=\"Test\" class=\"create\">first</div> and <DIV id=\"TEST\" class=\"delete\">second</div>";
Regex reg = new Regex(@"<div id=""(?-i:TEST)""[^>]*>[\w\s]+</div>", RegexOptions.IgnoreCase);
string str = "";
MatchCollection mc = reg.Matches(test);
foreach (Match m in mc)
{
str += m.Value + "\n";
}
Console.WriteLine(str);
/*--------输出--------
<DIV id="TEST" class="delete">second</div>
*/
环视基础
表达式
说明
(?<=Expression)
逆序肯定环视,表示所在位置左侧能够匹配Expression
(?<!Expression)
逆序否定环视,表示所在位置左侧不能匹配Expression
(?=Expression)
顺序肯定环视,表示所在位置右侧能够匹配Expression
(?!Expression)
顺序否定环视,表示所在位置右侧不能匹配Expression string str = "aa<p>one</p>bb<div>two</div>cc";
foreach (Match item in Regex.Matches(str, @"<(?!/?p\b)[^>]+>"))
{
Console.WriteLine(item.Value);
}
/*
<div>
</div>
*/
string str = "<div>a test</div><div>1</div>";
foreach (Match item in Regex.Matches(str, @"(?<=<div>)[^<]+(?=</div>)"))
{
Console.WriteLine(item.Value);
}
/*
a test
1
*/ double[] data = new double[] { , , , , , , , , , 12.345, 123.456, 1234.56, 12345.6789, 123456.789, 1234567.89, 12345678.9 };
string s = "";
foreach (double d in data)
{
s += "源字符串:" + d.ToString().PadRight() + "格式化:" + Regex.Replace(d.ToString(), @"(?<=\d)(?<!\.\d*)(?=(?:\d{3})+(?:\.\d+|$))", ",") + "\n";
}
Console.Write(s); 结果:
源字符串: 格式化:
源字符串: 格式化:
源字符串: 格式化:
源字符串: 格式化:,
源字符串: 格式化:,
源字符串: 格式化:,
源字符串: 格式化:,,
源字符串: 格式化:,,
源字符串: 格式化:,,,
源字符串:12.345 格式化:12.345
源字符串:123.456 格式化:123.456
源字符串:1234.56 格式化:,234.56
源字符串:12345.6789 格式化:,345.6789
源字符串:123456.789 格式化:,456.789
源字符串:1234567.89 格式化:,,567.89
源字符串:12345678.9 格式化:,,678.9 实现分析:
首先根据需求可以确定是把一些特定的位置替换为“,”,接下来就是分析并找到这些位置的规律,并抽象出来以正则表达式来表示。
、这个位置的左侧必须为数字
、这个位置右侧到出现“.”或结尾为止,必须是数字,且数字的个数必须为3的倍数
、这个位置左侧相隔任意个数字不能出现“.”
由以上三条,就可以完全确定这些位置,只要实现以上三条,组合一下正则表达式就可以了。
根据分析,最终匹配的结果是一个位置,所以所有子表达式都要求是零宽度。
、是对当前所在位置左侧附加的条件,所以要用到逆序环视,因为要求必须出现,所以是肯定的,符合这一条件的子表达式即为“(?<=\d)”
、是对当前所在位置右侧附加的条件,所以要用到顺序环视,也是要求出现,所以是肯定的,是数字,且个数为3的倍数,即“(?=(?:\d{})*)”,到出现“.”或结尾为止,即“(?=(?:\d{})*(?:\.|$))”
、是对当前所在位置左侧附加的条件,所以要用到逆序环视,因为要求不能出现,所以是否定的,即“(?<!\.\d*)”
因为零宽度的子表达式是非互斥的,最后匹配的都是同一个位置,所以先后顺序是不影响最后的匹配结果的,可以任意组合,只是习惯上把逆序环视写在左侧,顺序环视写在右侧。 贪婪与非贪婪模式匹配
string str = "aa<div>test1</div>bb<div>test2</div>cc";
foreach (Match item in Regex.Matches(str, @"<div>.*</div>"))
{
Console.WriteLine(item.Value);
}
/*贪婪模式,很好理解,尽量多匹配
<div>test1</div>bb<div>test2</div>
*/
如果正则表达式是<div>.*?</div>
/*非贪婪模式,也叫懒惰模式,就是懒的意思,匹配到了就不会再向后匹配
<div>test1</div>
<div>test2</div>
*/ 贪婪非贪婪的比较以及优化
string str =@"The phrase ""regular expression"" is called ""Regex"" for short.";
foreach (Match item in Regex.Matches(str, @""".*"""))
{
Console.WriteLine(item.Value);
}
一、使用”.*”这种匹配的结果不符合:"regular expression" is called "Regex"
二、使用”.*?”的结果:
"regular expression"
"Regex"
OK,不过进行了四次回溯
三、使用这种[^"]*匹配OK,没有回溯
四、对三的改进,固化分组 (?>[^"]*),匹配效率最好 再看一例,获取img标签的src内容:
string str =@"<img class=""test"" src=""/img/logo.gif"" title=""测试"" />";
foreach (Match item in Regex.Matches(str, @"<img\b.*?src=""(.*?)"".*?"))
{
Console.WriteLine(item.Groups[].Value);
}
使用的是非贪婪模式,我们通过排除型字符组转换为贪婪模式,提高匹配效率
@"<img\b.*?src=""([^""]*)""[^>]*"
img与src之间的非贪婪通过顺序环视来转化
@"<img\b(?:(?!src=).)*src=""([^""]*)""[^>]*"
“(?!src=).”表示这样一个字符,从它开始,右侧不能是字符序列“src=”,而“(?:(?!src=).)*”就表示符合上面规则的字符,有0个或无限多个。这样就达到排除字符序列的目的,实现的效果同排除型字符组一样,只不过排除型字符组排除的是一个或多个字符,而这种环视结构排除的是一个或多个有序的字符序列。 但是以顺序环视的方式排除字符序列,由于在匹配每一个字符时,都要进行较多的判断,所以相对于非贪婪模式,是提升效率还是降低效率,要根据实际情况进行分析。对于简单的正则表达式,或是简单的源字符串,一般来说是非贪婪模式效率高些,而对于数量较大源字符串,或是复杂的正则表达式,一般来说是贪婪模式效率高些。 PS:一般都是贪婪模式+固化分组,当然也需要看具体情况。 一些正则的实践篇
清除掉iframe+javascript+css的恶意脚本
http://rczjp.cn/HTML/101210/20105010115029.html
正则截取URL网址
http://rczjp.cn/HTML/101218/20102718032702.html
常见的正则
http://rczjp.cn/HTML/081119/20082119022155.html 此文章来自原作者:http://blog.csdn.net/lxcnn/ 具体详情去过客的blog参看。
下面是作者写的NFA引擎匹配原理,讲解的很详细,值得参看:
http://blog.csdn.net/lxcnn/archive/2009/06/28/4304651.aspx
C# 正则表达式及常用正则表达式的更多相关文章
- Javascript正则构造函数与正则表达字面量&&常用正则表达式
本文不讨论正则表达式入门,即如何使用正则匹配.讨论的是两种创建正则表达式的优劣和一些细节,最后给出一些常用正则匹配表达式. Javascript中的正则表达式也是对象,我们可以使用两种方法创建正则表达 ...
- C#正则表达式Regex常用匹配
使用Regex类需要引用命名空间:using System.Text.RegularExpressions; 利用Regex类实现验证 示例1:注释的代码所起的作用是相同的,不过一个是静态方法,一个是 ...
- [python] 常用正则表达式爬取网页信息及分析HTML标签总结【转】
[python] 常用正则表达式爬取网页信息及分析HTML标签总结 转http://blog.csdn.net/Eastmount/article/details/51082253 标签: pytho ...
- 常用正则表达式-copy
匹配中文:[\u4e00-\u9fa5] 英文字母:[a-zA-Z] 数字:[0-9] 匹配中文,英文字母和数字及_: ^[\u4e00-\u9fa5_a-zA-Z0-9]+$ 同时判断输入长度:[\ ...
- IOS常用正则表达式
IOS常用正则表达式 正则表达式用于字符串处理.表单验证等场合,实用高效.现将一些常用的表达式收集于此,以备不时之需. 匹配中文字符的正则表达式: [\u4e00-\u9fa5] 评注:匹配中文还真是 ...
- js常用正则表达式2
字符 含意 \ 做为转意,即通常在"\"后面的字符不按原来意义解释,如/b/匹配字符"b",当b前面加了反斜杆后/\b/,转意为匹配一个单词的边界. -或- 对 ...
- 【转】【Asp.Net MVC】asp.net mvc Model验证总结及常用正则表达式
本文属转载,来源: http://www.byywee.com/page/M0/S868/868615.html 关于Model验证官方资料: http://msdn.microsoft.com/zh ...
- PHP常用正则表达式汇总 [复制链接]
PHP常用正则表达式汇总 [复制链接] 上一主题下一主题 离线我是小猪头 法师 发帖 539 加关注 发消息 只看楼主 倒序阅读 使用道具楼主 发表于: 2011-06-22 更多 ...
- PHP中常用正则表达式大全
常用正则表达式大全!(例如:匹配中文.匹配html) 匹配中文字符的正则表达式: [u4e00-u9fa5] 评注:匹配中文还真是个头疼的事,有了这个表达式就好办了 匹配双字节字符(包括汉字在内 ...
随机推荐
- 监控页面所有 ajax请求
监控所有ajax请求: 你是不是有遇到这样的问题:页面发起两个ajax请求,希望它们都成功以后,再做一个动作? 很容易想到的解决方案是,等其中一个结束以后,再发起另外一个,这个过程用回调函数来完成. ...
- 小白也能用Git管理团队项目了:百度云同步+Git Extensions+Git Source Control Provider
百度云同步 百度云同步,会将本地的某个文件目录和云端进行同步.如果在本地将这个同步的目录设置为Git的中心服务器,那么本地push到中心服务器的内容也会被同步到云端.其他开发者只要也进行相同的设置,就 ...
- semanage: 未找到命令
[root@hn ~]# semanage port -l|grep ssh-bash: semanage: 未找到命令 yum -y install policycoreutils-python
- (转)zookeeper学习记录--附browser
转自:http://agapple.iteye.com/blog/1111377 背景 前段时间看了S4流计算引擎,里面使用到了zookeeper进行集群管理,所以也就花了点时间研究了下zookeep ...
- PHPStorm+XDebug进行调试图文教程以及解析wamp的php.ini设置不生效的原因
这篇文章主要为大家详细介绍了PHPStorm+XDebug进行调试图文教程,内容很丰富,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 笔者的开发环境如下:Windows8.1+Apache+P ...
- Java线程新特性--- Lock
在Java5中,专门提供了锁对象,利用锁可以方便的实现资源的封锁,用来控制对竞争资源并发访问的控制,这些内容主要集中在java.util.concurrent.locks包下面,里面有三个重要的接口C ...
- 官网下载jdk
http://www.oracle.com/technetwork/java/javase/downloads/index.html 第一步: 第二步:默认是显示最新版本的,如果需要其他版本的,拖到最 ...
- 查看用户的SQL执行历史
程序开发少不来SQL,基本都是基于SQL开发,程序仅仅起一个流程控制的作用.但是数据库本身存在许多内置的视图或者内置的表,如果打算研究SQL执行的效率已经SQL执行的历史记录,通过这些视图可以知道. ...
- Sqoop2入门之导入关系型数据库数据到HDFS上
需求:将hive数据库中的TBLS表导出到HDFS之上: $SQOOP2_HOME/bin/sqoop.sh client sqoop:> set server --host hadoop000 ...
- 前端开发 Grunt 之 Connect
在前端开发过程中,我们需要在开发过程中,将开发中的站点部署到服务器上,然后,在浏览器中查看实际的效果,在 Grunt 环境下,可以直接使用集成在 Grunt 中的 Connect 插件完成站点服务器的 ...