组之间的正则表达式替换
Posted
技术标签:
【中文标题】组之间的正则表达式替换【英文标题】:Regex Replace between groups 【发布时间】:2017-12-10 08:58:51 【问题描述】:所以我在 C# 中有以下 regex.replace:
Regex.Replace(inputString, @"^([^,]*,)5(.*)", @"$1somestring,$2");
其中5
是代码中的可变数字,但这并不真正相关,因为在执行时它总是有一个设定值(例如5
)。与somestring,
相同。
基本上我想在两组之间输入somestring,
。输出适用于somestring,$2
,但$1
只是打印为$1
。所以说无论(.*)
抓住= "2, a, f2"
,我得到的结果字符串都是$1somestring,2,a,f2
,不管$1
是什么。这是因为重复组功能5
?如果是这样,我如何获取重复集合并将其放在我现在拥有 $1
的位置?
编辑:我知道第一组也正确捕获。我使用这个正则表达式获取somestring,
的内容:
Regex.Match(line, @"^([^,]*,)5([0-9]+\.[0-9]+),.*");
第一部分与替换正则表达式中的第一组相同,并且工作正常,所以应该没有问题(并且它们都用于同一个字符串)。
编辑2:
好的,我将尝试解释更多的过程,因为有人说这很难理解。我有三个变量,line
一个我使用的字符串,latIndex
和 lonIndex
只是整数(告诉我在什么之间,我寻找的两个双打位于)。我有以下两个匹配项:
var latitudeMatch = Regex.Match(line, @"^([^,]*,)" + latIndex + @"([0-9]+\.[0-9]+),.*");
var longitudeMatch = Regex.Match(line, @"^([^,]*,)" + lonIndex + @"([0-9]+\.[0-9]+),.*");
然后我抓住双打:
var latitude = latitudeMatch.Groups[2].Value;
var longitude = longitudeMatch.Groups[2].Value;
我使用这些双精度从 Web API 获取字符串,并将其存储在名为 veiRef
的变量中。然后我想在双打之后插入这些,使用以下代码(在纬度或经度之后插入,具体取决于最后出现的那个):
if (latIndex > lonIndex)
line = Regex.Replace(line, @"^([^,]*,)" + (latIndex+1) + @"(.*)",$@"$1veiRef,$2");
else
line = Regex.Replace(line, @"^([^,]*,)" + (lonIndex + 1) + @"(.*)", $@"$1veiRef,$2");
但是,这会导致字符串 line
之前没有插入 $1 的内容($2 可以正常工作)。
【问题讨论】:
只是一个习惯/代码标准。除非我 想要 使用特殊的字符串字符,否则我基本上都会使用它。这是一个 C# 转义字符(表示逐字字符串)。 只是为了澄清,有人问我为什么在替换字符串中使用@。只是把它放在这里以防有人对我为什么写那条评论感到困惑。 我有一个这样的字符串:a, s, f, double, double, 12, sd, 1
。双打出现的地方是动态的,我想在双打之后插入一些东西(因此5
是一个变量)。所以使用这个例子,我想把字符串变成:a, s, f, double, double, somestring, 12, sd, 1
.
所以实际上我的正则表达式看起来像这样:var latitudeMatch = Regex.Match(line, @"^([^,]*,)" + latIndex + @"([0-9]+\.[0-9]+),.*");
和 line = Regex.Replace(line, @"^([^,]*,)" + (latIndex+1) + @"(.*)",$@"$1veiRef$2");
..为什么我回答后人们总是删除他们的 cmets
【参考方案1】:
您在模式的开头有一个重复的捕获组,您需要将其转换为非捕获组并用捕获组包装。然后,您可以使用$1
反向引用访问整个匹配部分。
var line = "a, s, f, double, double, 12, sd, 1";
var latIndex = 5;
var pat = $@"^((?:[^,]*,)latIndex+1)(.*)";
// Console.WriteLine(pat); // => ^((?:[^,]*,)6)(.*)
var veiRef = "str";
line = Regex.Replace(line, pat, $"$1veiRef.Replace("$","$$")$2");
Console.WriteLine(line); // => a, s, f, double, double, 12,str sd, 1
见C# demo
模式 - ^((?:[^,]*,)6)(.*)
- 现在在 ^
之后包含 ((?:[^,]*,)6)
,这就是现在 $1
在找到匹配项后所保留的内容。
由于您的替换字符串是动态的,您需要确保其中的任何 $
都加倍(因此,.Replace("$","$$")
)并且第一个反向引用是明确的,因此它应该看起来像 $1
(无论如何它都可以工作) veiRef
是否以数字开头)。
详细替换字符串:
它是一个内插字符串文字...$"
- 内插字符串文字的声明(开始)
$1
- 文字 $1
字符串(
和
必须加倍以表示文字符号)
veiRef.Replace("$","$$")
- 内插字符串文字内的一段 C# 代码(我们用单个 ...
分隔允许代码的这部分)
$2
- 文字 $2
字符串
"
- 内插字符串文字的结尾。
【讨论】:
使用这个:line = Regex.Replace(line, pat, $"$1veiRef.Replace("$", "$$")$2");
似乎有效。我以前没有见过这种语法。你能解释一下吗?
谢谢。我还是不太明白(关于将一些符号加倍的东西,以及为什么 2 美元不需要它,但其余的却是必要的),但我想我稍后会尝试再次查看它。
如果您替换为$1$5$2
并期望在结果中出现文字$5
(您的veiRef
内容),您需要在替换字符串中加倍$
符号。如果veiRef
以数字开头,则必须使用明确的反向引用语法$1
。例如。如果您的veiRef
以3
开头,您的替换将看起来像$13
并且正则表达式引擎将在您的模式中寻找第13 个捕获组,但不会找到它,对吧? $13
将替换为第一个捕获组,然后替换为 3
。
veiRef
字符串和 $1 确实都以数字开头。所以 $1 = $1 只是它适用于以数字开头的字符串?还是 $1 = 逐字 $1?如果是这样,那么我现在明白这部分了。为什么用 $$ 替换 veiRef 中的每个 $?
$1
持有什么并不重要。 $1
和 $15
的工作方式不同,您可以使用 veiRef
进行一些测试,例如 5stars
。 “逐字逐句”不是您可以在这里使用的术语。毫不含糊。至于在替换字符串中替换美元,只知道用美元替换,它应该是双倍的。作为测试,只需替换,无需将额外的 Replace 和 veiRef 设置为 price: $1
。【参考方案2】:
在重复捕获组周围添加一个额外的组似乎可以为您提供的示例提供所需的输出。
Regex.Replace("a, s, f, double, double, 12, sd, 1", @"^(([^,]*,)5)(.*)", @"$1somestring,$3");
我不是 RegEx 方面的专家,可能有人可以比我更好地解释它,但是:-
组 1 是一组 5 个重复捕获组 第 2 组是最后一个重复捕获组 第 3 组是 5 个重复捕获组之后的文本。
【讨论】:
嗯似乎对我不起作用。我将我的正则表达式编辑为line = Regex.Replace(line, @"^(([^,]*,)" + (latIndex+1) + @")(.*)",$@"$1veiRef$2");
为清楚起见,在使用我正在使用的测试输入进行编译后,该正则表达式中的模式变为"^(([^,]*,)6)(.*)"
。它仍然把结果变成$1somestring,[value of (.*)]
编辑:刚刚错过了 3 美元……抱歉。但是在正则表达式中使用 $3 的行为仍然相同。 $1 仍然打印为 $1,而不是实际值。
奇怪,你是正则表达式为我工作!!。另一种选择,您是否尝试过命名组? Regex.Replace("a, s, f, double, double, 12, sd, 1", @"^(?<begining>([^,]*,)5)(?<end>.*)", @"$beginingsomestring,$end");
如果您查看另一个答案,我的问题似乎是我的插入字符串(以及第一组)在我的情况下都以数字开头,这导致正则表达式翻转。我不知道 $groups 不是逐字记录的。以上是关于组之间的正则表达式替换的主要内容,如果未能解决你的问题,请参考以下文章