以下代码......
using System;
using System.Text.RegularExpressions;
public class Program
{
public static void Main()
{
var r = new Regex("(.*)");
var c = "XYZ";
var uc = r.Replace(c, "A $1 B");
Console.WriteLine(uc);
}
}
产生以下输出......
XYZ BA B
你认为这是对的吗?
输出不应该......
XYZ B
我想我在这里做了些蠢事。如果您能帮助我理解这个问题,我将不胜感激。
这是有趣的事情......
using System;
using System.Text.RegularExpressions;
public class Program
{
public static void Main()
{
var r = new Regex("(.*)");
var c = "XYZ";
var uc = r.Replace(c, "$1");
Console.WriteLine(uc);
}
}
...输出
XYZ
答案 0 :(得分:6)
至于为什么引擎返回2个匹配,这是由于.NET(也是Perl和Java)处理全局匹配的方式,即在输入字符串中找到与给定模式的所有匹配。
该过程可以描述如下(当前索引通常在搜索开始时设置为0,除非指定):
$0
)非空(至少消耗了一个字符),请添加结果并将当前索引设置为主匹配($0
)的结尾。然后转到第1步。$0
)为空:
引擎需要检查空匹配;否则,它将以无限循环结束。设计师会识别空匹配的使用(例如,将字符串拆分为字符),因此必须将引擎设计为避免永远卡在某个位置。
此过程解释了为什么在结尾处存在空匹配:因为在(.*)
匹配abc
和(.*)
之后在字符串末尾(索引3)进行搜索可以匹配空字符串,找到空匹配。并且引擎不会产生无限数量的空匹配,因为最后已经找到了空匹配。
a b c
^ ^ ^ ^
0 1 2 3
第一场比赛:
a b c
^ ^
0-----3
第二场比赛:
a b c
^
3
使用上面的全局匹配算法,从同一个索引开始最多只能有2个匹配,这种情况只有在第一个是空匹配时才会发生。
请注意,如果主匹配为空,JavaScript只会将当前索引增加1,因此每个索引最多匹配1个匹配项。但是,在这种情况下(.*)
,如果使用全局标记g
进行全局匹配,则会发生相同的结果:
(下面的结果来自Firefox,请注意g
标志)
> "XYZ".replace(/(.*)/g, "A $1 B")
"A XYZ BA B"
答案 1 :(得分:4)
答案 2 :(得分:4)
你的正则表达式有两个匹配,替换将替换它们。第一个是“XYZ”,第二个是空字符串。我不确定的是为什么它首先有两场比赛。您可以使用^(。*)$来修复它,以强制它考虑字符串的开头和结尾。
或者使用+
代替*
强制它匹配至少一个字符。
.*
匹配空字符串,因为它没有字符。
.+
与空字符串不匹配,因为它需要至少一个字符。
有趣的是,在Javascript(在Chrome中):
var r = /(.*)/;
var s = "XYZ";
console.log(s.replace(r,"A $1 B");
将输出预期的A XYZ B
而没有虚假的额外匹配。
编辑(感谢@nhahtdh):但是将g
标志添加到Javascript正则表达式中,给出与.NET中相同的结果:
var r = /(.*)/g;
var s = "XYZ";
console.log(s.replace(r,"A $1 B");
答案 3 :(得分:4)
*
量词匹配0或更多。这导致有2场比赛。 XYZ什么都没有。
尝试+
量词,而不是匹配1或更多。
一个简单的解释是查看这样的字符串:XYZ<nothing>
XYZ
和<nothing>
XYZ
替换为A $1 B
($ 1在这里XYZ
)结果:A XYZ B
<nothing>
替换为A $1 B
($ 1在这里<nothing>
)结果:A B
最终结果:A XYZ BA B
为什么<nothing>
本身就是一个匹配很有趣,而且我并没有真正考虑过这个问题。 (为什么没有无限<nothing>
个匹配?)
答案 4 :(得分:2)
正则表达式是一种特殊的语言。你必须准确理解什么(。*)将匹配。你还需要了解贪婪。
(。*)将贪婪地匹配0个或更多字符。因此,在字符串"XYZ"
中,它会将整个字符串与其第一个匹配项匹配,并将其置于$ 1位置,为您提供:
XYZ B.
然后它将继续尝试匹配并匹配字符串末尾的null
,将$ 1设置为null,为您提供:
A B. 导致您看到的字符串:
XYZ BA B
如果您想限制贪婪并匹配每个角色,您可以使用以下表达式:
(。*?)
这将分别匹配每个字符X,Y和Z,以及最后的null
,结果如下:
A BXA BYA BZA B
如果您不希望正则表达式超出给定字符串的范围,请使用^
和$
标识符限制正则表达式。
为了让您更好地了解正在发生的事情,请考虑此测试以及生成的匹配组。
[TestMethod()]
public void TestMethod3()
{
var myText = "XYZ";
var regex = new Regex("(.*)");
var m = regex.Match(myText);
var matchCount = 0;
while (m.Success)
{
Console.WriteLine("Match" + (++matchCount));
for (int i = 1; i <= 2; i++)
{
Group g = m.Groups[i];
Console.WriteLine("Group" + i + "='" + g + "'");
CaptureCollection cc = g.Captures;
for (int j = 0; j < cc.Count; j++)
{
Capture c = cc[j];
Console.WriteLine("Capture" + j + "='" + c + "', Position=" + c.Index);
}
}
m = m.NextMatch();
}
输出:
Match1
Group1='XYZ'
Capture0='XYZ', Position=0
Group2=''
Match2
Group1=''
Capture0='', Position=3
Group2=''
请注意,有两个匹配的组。第一个是整个组XYZ,第二个是空组。然而,有两组相匹配。因此,在第一种情况下,1美元换成XYZ,第二种情况换成null
。
另请注意,正斜杠/
只是.net正则表达式引擎中考虑的另一个字符,没有特殊含义。 javascript解析器处理/
的方式不同,因为它必须因为它存在于HTML解析器的框架中,其中</
是一个特殊考虑因素。
最后,为了得到你真正想要的东西,请考虑这个测试:
[TestMethod]
public void TestMethod1()
{
var r = new Regex(@"^(.*)$");
var c = "XYZ";
var uc = r.Replace(c, "A $1 B");
Assert.AreEqual("A XYZ B", uc);
}