正则表达式能否捕获文本块中的多个相似模式?PowerShell下多匹配结果提取异常问题咨询
解决PowerShell正则捕获重复命名组只保留最后一项的问题
问题场景
我最近需要从一批游戏音效配置文本里提取数据,文本格式是这样的:
soundeffect = { name = event_ship_explosion sounds = { sound = event_ship_explosion } volume = 0.2 max_audible = 1 max_audible_behaviour = fail } soundeffect = { name = event_ship_bridge sounds = { sound = event_ship_bridge sound = event_ship_bridge_02 sound = event_ship_bridge_03 } volume = 0.3 max_audible = 1 max_audible_behaviour = fail }
我的需求很明确:每个soundeffect块里,把name对应的值和所有sound条目对应的值关联起来,比如第二个块要得到event_ship_bridge对应三个sound值的集合。
一开始我写了这么个正则:
name\s*=\s*(?<Group>\w+)\s*sounds\s*=\s*{\s*(?>\s*sound\s*=\s*(?<Sound>\w+)\s*)+}
在测试工具上明明能看到Sound组有3个匹配结果,但放到PowerShell里用Select-String跑的时候,却只保留了最后一个sound值,而且感觉-AllMatches参数没生效,结果完全不符合预期。
问题根源
后来我才搞明白:PowerShell的Select-String在处理命名捕获组时,默认只会保留该组最后一次匹配的内容,不会自动收集所有重复匹配的项。而且我的正则把整个sounds块作为一个整体匹配,内部的Sound组重复匹配不会被单独记录下来,自然拿不到所有结果。
两种可行解决方案
方案一:分块处理(简单易理解)
先把每个soundeffect块单独匹配出来,再逐个块提取name和所有sound值,逻辑清晰不容易出错:
# 第一步:匹配所有完整的soundeffect块 $blockRegex = 'soundeffect\s*=\s*{[^}]+}' $blocks = $Text | Select-String -Pattern $blockRegex -AllMatches | ForEach-Object { $_.Matches.Value } # 第二步:逐个处理块,提取目标数据 $result = $blocks | ForEach-Object { $currentBlock = $_ # 提取name的值 $groupName = ($currentBlock | Select-String -Pattern 'name\s*=\s*(\w+)' -AllMatches).Matches.Groups[1].Value # 提取所有sound的值 $soundList = ($currentBlock | Select-String -Pattern 'sound\s*=\s*(\w+)' -AllMatches).Matches.Groups[1].Value # 返回自定义对象 [PSCustomObject]@{ Group = $groupName Sound = $soundList } } # 查看结果 $result
运行后得到的结果完全符合预期,第二个块的输出是:
Group Sound ----- ----- event_ship_bridge {event_ship_bridge, event_ship_bridge_02, event_ship_bridge_03}
方案二:借助.NET正则的Captures属性(进阶)
如果你想用单个正则实现,可以直接调用.NET的[regex]类,它的Captures属性会记录命名组的所有匹配项,而不是只保留最后一个:
# 定义正则,(?s)开启单行模式,适配可能有换行的配置文本 $regex = [regex]'(?s)name\s*=\s*(?<Group>\w+)\s*sounds\s*=\s*{\s*(?:\s*sound\s*=\s*(?<Sound>\w+)\s*)+}' # 执行匹配 $allMatches = $regex.Matches($Text) # 处理匹配结果,提取所有捕获项 $result = $allMatches | ForEach-Object { $groupName = $_.Groups['Group'].Value # 通过Captures获取Sound组的所有匹配值 $soundList = $_.Groups['Sound'].Captures | ForEach-Object { $_.Value } [PSCustomObject]@{ Group = $groupName Sound = $soundList } } # 查看结果 $result
这个方案更简洁,适合熟悉.NET正则细节的场景,同样能得到完整的关联数据。
后续处理
得到这个对象数组后,你可以直接用ConvertFrom-Text或者其他工具进一步处理,比如导出为CSV、JSON格式,或者直接在PowerShell里进行筛选、统计操作。
内容的提问来源于stack exchange,提问作者Tuttu
相关产品推荐
相关产品推荐

