假设我拥有以下正则表达式,但是我将其加载到变量$regex中,并且在设计时不知道其内容,但是在运行时可以发现它包含“version1”、“version2”、“version3”和“version4”命名组:
用户可以指定一个文件集和一个正则表达式来匹配包含字段的行,最初的想法是通过命名组捕获各个字段。工具具有应替换文件中的各个版本字段值,但必须保留将包含替换的原始行的原始格式,并仅替换请求的字段。
编辑-2: 我认为我可以通过基于每个匹配项的位置和范围的子字符串计算获得所需的结果,但希望Powershell的替换操作能为我节省一些工作。
编辑-3: 因此,正如Ansgar在下面正确而简洁地描述的那样,没有办法(仅使用原始输入字符串,关于其中命名组的正则表达式以及生成的匹配项)使用“-replace”操作(或其他正则表达式操作)来执行命名组的捕获的替换,同时保留原始字符串的其余部分。对于这个问题,如果有人感兴趣,我最终采用了以下解决方案。YMMV,其他解决方案可能存在。非常感谢Ansgar提供的反馈和选项。
在以下代码块中: $input是要进行替换的文本行 $regex是从文件中读取的正则表达式(类型为[string]),已经验证至少包含受支持的命名组之一 $regexToGroupName是一个哈希表,将正则表达式字符串映射到按[regex] :: GetGroupNames()返回的数组的顺序排序的组名数组,该数组与它们在表达式中出现的从左到右的顺序匹配 $groupNameToVersionNumber是一个哈希表,将组名映射到版本号。 $regex中的命名组的限制仅为(我认为)命名组中的表达式不能嵌套,并且应在输入字符串中最多匹配一次。
"Version (?<version1>\d),(?<version2>\d),(?<version3>\d),(?<version4>\d)"
...而我有这些变量:
$version1 = "3"
$version2 = "2"
$version3 = "1"
$version4 = "0"
我在文件中遇到了以下字符串:
Version 7,7,0,0
如何将存储在变量$input中的$regex命名组的值替换为$version1、$version2、$version3、$version4的值,如果我不知道它们在$regex中出现的顺序(我只知道$regex包括这些命名组)?
我找不到任何描述使用组名称作为匹配索引来用变量的值替换命名组语法的参考资料 - 这个功能是否受支持?
编辑: 澄清一下 - 目标是在任何类型的文本文件中替换模板化的版本字符串,其中给定文件中的版本字符串需要替换变量数量的版本字段(可以是2、3或所有4个字段)。例如,文件中的文本可能看起来像以下任何一种(但不仅限于此):
#define SOME_MACRO(4, 1, 0, 0)
Version "1.2.3.4"
SomeStruct vs = { 99,99,99,99 }
用户可以指定一个文件集和一个正则表达式来匹配包含字段的行,最初的想法是通过命名组捕获各个字段。工具具有应替换文件中的各个版本字段值,但必须保留将包含替换的原始行的原始格式,并仅替换请求的字段。
编辑-2: 我认为我可以通过基于每个匹配项的位置和范围的子字符串计算获得所需的结果,但希望Powershell的替换操作能为我节省一些工作。
编辑-3: 因此,正如Ansgar在下面正确而简洁地描述的那样,没有办法(仅使用原始输入字符串,关于其中命名组的正则表达式以及生成的匹配项)使用“-replace”操作(或其他正则表达式操作)来执行命名组的捕获的替换,同时保留原始字符串的其余部分。对于这个问题,如果有人感兴趣,我最终采用了以下解决方案。YMMV,其他解决方案可能存在。非常感谢Ansgar提供的反馈和选项。
在以下代码块中: $input是要进行替换的文本行 $regex是从文件中读取的正则表达式(类型为[string]),已经验证至少包含受支持的命名组之一 $regexToGroupName是一个哈希表,将正则表达式字符串映射到按[regex] :: GetGroupNames()返回的数组的顺序排序的组名数组,该数组与它们在表达式中出现的从左到右的顺序匹配 $groupNameToVersionNumber是一个哈希表,将组名映射到版本号。 $regex中的命名组的限制仅为(我认为)命名组中的表达式不能嵌套,并且应在输入字符串中最多匹配一次。
# This will give us the index and extent of each substring
# that we will be replacing (the parts that we will not keep)
$matchResults = ([regex]$regex).match($input)
# This will hold substrings from $input that were not captured
# by any of the supported named groups, as well as the replacement
# version strings, properly ordered, but will omit substrings captured
# by the named groups
$lineParts = @()
$startingIndex = 0
foreach ($groupName in $regexToGroupName.$regex)
{
# Excise the substring leading up to the match for this group...
$lineParts = $lineParts + $input.Substring($startingIndex, $matchResults.groups[$groupName].Index - $startingIndex)
# Instead of the matched substring, we'll use the substitution
$lineParts = $lineParts + $groupNameToVersionNumber.$groupName
# Set the starting index of the next substring that we will keep...
$startingIndex = $matchResults.groups[$groupName].Index + $matchResults.groups[$groupName].Length
}
# Keep the end of the original string (if there's anything left)
$lineParts = $lineParts + $input.Substring($startingIndex, $input.Length - $startingIndex)
$newLine = ""
foreach ($part in $lineParts)
{
$newLine = $newLine + $part
}
$input= $newLine