Board logo

标题: [问题求助] PowerShell怎样用正则匹配成对的括号? [打印本页]

作者: 小白龙    时间: 2023-3-11 21:01     标题: PowerShell怎样用正则匹配成对的括号?

xzc abc(s "(abc)"); dfg()

我想取上面红色字部分, 但是用下面的正则, 会串了括号, 把后面的也包括进来了

我要匹配与abc后面那个(成对的括号, 对这种正则一直没搞懂, 请路过高手帮忙, 谢谢

abc\(.+\)
  1. $s='xzc abc(s "(abc)"); dfg()'
  2. $s -replace 'abc\(.+\)'
复制代码

作者: went    时间: 2023-3-11 21:59

abc\([^\)]*[^\(]*\)
作者: WHY    时间: 2023-3-11 22:19

这个需要用到所谓的 "平衡组"
https://learn.microsoft.com/zh-c ... ng_group_definition
  1. $s='xzc abc(s "(abc)"); dfg()';
  2. [regex]::Match($s, 'abc\((?:(?>[^()]*)|\((?<Open>)|\)(?<-Open>))*(?(Open)(?!))\)').Value;
复制代码

作者: 小白龙    时间: 2023-3-11 22:53

回复 2# went


    多谢大佬,

我把正则放到了字符串中让正则处理, 就不行了
  1. $s='xzc abc(s "\([^\)]*[^\(]*\)"); dfg()'
  2. $s -replace 'abc\([^\)]*[^\(]*\)'
复制代码

作者: 小白龙    时间: 2023-3-11 22:56

回复 3# WHY

多谢大佬,
灵了, 能处理下面这个复杂的情况, 就是正则太长了, 没精简点的吗?
  1. $s='xzc abc(s "\([^\)]*[^\(]*\)"); dfg()'
  2. [regex]::Match($s, 'abc\((?:(?>[^()]*)|\((?<Open>)|\)(?<-Open>))*(?(Open)(?!))\)').Value;
复制代码

作者: 小白龙    时间: 2023-3-11 22:58

回复 3# WHY

另外, 我在另一个软件里执行会报错, 应该是不支持 <Open> 这种写法, 怎样更通用一些? 多谢
作者: Five66    时间: 2023-3-11 23:48

像这种匹配成对括号的要正则引擎的支持,很复杂也可能不准确,建议先获取包含最外层左括号的字符串,然后手动解析字符串,找到匹配的最外层右括号的位置后截取字符串
作者: WHY    时间: 2023-3-11 23:50

回复 6# 小白龙


      我要是你,我会打开链接,先了解一下什么是"平衡组";
或者搜索一下网络,搞明白那些流派支持 "平衡组",那些不支持。
像你这样,只想着要答案,一百年也学不会。
作者: 523066680    时间: 2023-3-12 00:07

本帖最后由 523066680 于 2023-3-12 00:23 编辑

其实我看题主四楼和五楼的例子都可以用  );  来限定末尾。
当然更复杂的情况且有必要的话可以考虑设计grammar来解决

抄了一段perl6的文法案例
  1. grammar pair {
  2.     token TOP     { <func> }
  3.     rule func    { <name> '(s' <.ws> <string> ')' }
  4.     rule name    { [\w]+ }
  5.     rule string  {
  6.         (:ignoremark \") ~ \"
  7.         [
  8.             \w |
  9.             [ '\\' <[\\/bfnrt"():]> ] |
  10.             <-[\\\"\n\t]>+
  11.         ]*
  12.     }
  13. };
  14. my $match = pair.parse('abc(s "\\:\/())))abc\([^\)]*[^\(]*\)")');
  15. say $match;
复制代码
结果
  1. 「abc(s "\:\/())))abc\([^\)]*[^\(]*\)")」
  2. func => 「abc(s "\:\/())))abc\([^\)]*[^\(]*\)")」
  3.   name => 「abc」
  4.   string => 「"\:\/())))abc\([^\)]*[^\(]*\)"」
  5.    0 => 「"」
复制代码
并没有解决问题 (逃
作者: 小白龙    时间: 2023-3-12 07:10

本帖最后由 小白龙 于 2023-3-12 07:16 编辑

回复 8# WHY

多谢大佬指导,

我也看了一下那个平衡组的链接, 但是水平有限实在看不懂, 楼2的正则我能理解, 想着就是它了, 没想到它没有彻底解决问题, 大佬的正则不能看懂, 但确实能解决各种情况

我不是专业的程序员, 就是偶尔改改现成的代码, 感觉没有一定积累, 看懂平衡组真是有点难度

现在遇到的问题是, 我的那个程序不支持保存到 <Open> 这种命名的组中 我把它改成数字组的形式, 还是不行, 其实还是自己没能理解整行正则的含义, 生搬硬套

大佬能再帮一把就太感谢了, 不是不想自己解决, 是实在积累不够, 看不懂

可能看的情况多了, 在某个时刻会豁然开朗, 就把它理解了
作者: 小白龙    时间: 2023-3-12 07:22

回复 7# Five66

感谢指导, 以前遇也改过成对的<>的代码, 好像没有()这么复杂

用3楼大佬的, 确实能适应各种情况了,但是有的软件不支持这种写法, 搜索了一下, 说是这种<>号的组命名不支持, 改成数字的就可以了, 但是水平有限还是没有搞定
作者: 小白龙    时间: 2023-3-12 10:57

也找到一篇非常不错的关于捕获组的文章, 写的也真是太用心了, 有前因, 有后果, 有动图, 大部分能看懂, 但是还是不会用,

https://www.cnblogs.com/piperck/p/15878834.html
作者: idwma    时间: 2023-3-12 18:18

那个程序不支持平衡组,可以试试用递归呀
作者: 小白龙    时间: 2023-3-12 18:59

本帖最后由 小白龙 于 2023-3-12 19:00 编辑

回复 13# idwma
多谢大佬指导,

应该不是不支持平衡组, 而不是不支持那种命名,

另外, 怎么递归呀, 不太懂, 能帮一把吗? 多谢

或者用别的办法也行
作者: idwma    时间: 2023-3-12 21:23

本帖最后由 idwma 于 2023-3-12 21:28 编辑

回复 14# 小白龙
  1. $s='xzc abc(s "\([^\)]*[^\(]*\)"); dfg()'
  2. $t='abc'
  3. $a=0
  4. 0..$s.length|%{
  5.     if(($s[$_..($_+$t.length-1)] -join '') -eq $t){
  6. $_..$s.length|%{
  7.     if($f -eq $null -and $s[$_] -eq '('){$f=1;$b=$_}
  8.     if($f -eq 1){
  9. if($s[$_] -eq '('){$a+=1}elseif($s[$_] -eq ')'){$a-=1}
  10.                 if($a -eq 0){$c=$_;break}
  11.     }
  12. }
  13.     }
  14. }
  15. $t+($s[$b..$c] -join '')
复制代码

作者: 小白龙    时间: 2023-3-12 21:55

回复 15# idwma

虽然看不懂, 但还是非常感谢!

难道我的问题, 用正则, WHY大佬的思路是唯一正解了吗?  不能在went大佬的正则基础上改改吗?
作者: WHY    时间: 2023-3-12 22:51

本帖最后由 WHY 于 2023-3-13 23:40 编辑
  1. Function Get-BalanceSet([ref]$str){
  2.     $index = $str.Value.IndexOf('abc(');
  3.     if ($index -lt 0){         #不含 'abc(' 子串,退出函数
  4.         return;
  5.     }
  6.     $str.Value = $str.Value.SubString($index);   #删掉 'abc(' 之前的所有字符
  7.     $arr = [Collections.ArrayList]@();
  8.   
  9.     $n = 0;
  10.     $m = 0;
  11.     for ($i=0; $i -lt $str.Value.Length; $i++){  #遍历字符串每个字符
  12.         $s1 = $str.Value.SubString($i, 1);       #截取第i个字符,赋值给$s1
  13.         [void]$arr.Add($s1);                     #再将$s1存放到数组
  14.         if ( $s1 -eq ')' ){                      #右括弧,n减1
  15.             $n--;
  16.         } elseIf ( $s1 -eq '(' ){                #左括弧,n加1
  17.             $n++;
  18.             $m++;
  19.         }
  20.         if ($n -eq 0 -and $m -gt 0){             #若n=0且左括弧数不为0
  21.             [void]$out.Add($arr -join '');       #匹配结果存放到$out
  22.             break;                               #退出For
  23.         }
  24.     }
  25.     $str.Value = $str.Value.SubString(4);        #删除开头的 'abc(' 4个字符
  26.     Get-BalanceSet ([ref]$str.Value);            #递归
  27. }
  28. $s = 'xzc abc(s "(abc)"); dfg(); abc(123(ggg))';
  29. $out = [Collections.ArrayList]@();               #数组,存放结果
  30. Get-BalanceSet ([ref]$s);
  31. $out;
  32. echo 'Done';
  33. [Console]::ReadLine();
复制代码

作者: 小白龙    时间: 2023-3-13 10:38

回复 17# WHY


    多谢大佬, 太细心了, 感谢
作者: Five66    时间: 2023-3-13 17:43

说一下吧,正则匹配只有3种情况:
0次(包括多次0次)
最少次
最多次

字符串'xzc abc(s "(abc)"); dfg()' 中的 'abc(' 可以通过最少次1次匹配得到
但是与 'abc(' 中的左括号匹配的右括号是不属于上面三种情况
0次就是匹配不到
最少次会匹配到 'abc)'中的右括号
最多次会匹配到 'dfg()' 中的右括号
指定次数虽然可能匹配得到,但是不通用,除非所有需要匹配的都是所指定的次数

平衡组是在正则引擎的支持上实现这种不属于三种情况匹配的通用解决方案之一
平衡组其实就是命名组的自动计数,匹配到左括号就计数加1,匹配到右括号就计数减1,最后判断计数是否为0(或偶数)来判断是否匹配

也可以手动根据平衡组的实现来手动匹配,例如
  1. $s='xzc abc(s "(abc)"); dfg()'
  2. $ss=[regex]::Match($s, 'abc\(.+\)').value
  3. $a=[Collections.ArrayList]::new()
  4. $a.Add(-1)
  5. (0..($ss.Length-1)).ForEach({
  6. if($ss[$_] -eq '('){$a.Add($_)}
  7. if($ss[$_] -eq ')'){
  8.     $a.Removeat($a.Count-1)
  9.     if($a.Count -eq 1){$a[0]=$_;break}
  10. }
  11. })
  12. [Console]::WriteLine($ss.Substring(0,$a[0]+1))
复制代码

作者: terse    时间: 2023-3-13 18:41

只能搞简单的匹配 复杂的不能
  1. $s='xzc abc(s "(abc)"); dfg()'
  2. [regex]::Match($s, '(abc\([^\)\(]*)(\(.*\))*([^\)\(]*\))').Value;
复制代码

作者: 小白龙    时间: 2023-3-13 20:05

本帖最后由 小白龙 于 2023-3-13 20:07 编辑

回复 20# terse


    大佬实在是高, 这个正则兼容性不错, 不支持平衡组的软件也能用, 目前还没遇到匹配不到的

难道还有匹配不了的情况吗? 能说说吗? 怕到时候掉坑里
作者: terse    时间: 2023-3-13 20:45

回复 21# 小白龙
当出现如下的情况,便匹配不能,要修改这样
当然还会出现其他状况 所以不通用
  1. $s = 'xzc abc(s "(abc)");a, d(fg())'
  2. [regex]::Match($s, '(abc\([^\)\(]*)(\(.*?\))*([^\)\(]*\))').Value;
复制代码

作者: 小白龙    时间: 2023-3-13 20:50

回复 22# terse


    哦, 看来WHY大佬的平衡组正则式, 确实是唯一正解了, 能适应各种情况
作者: terse    时间: 2023-3-13 22:24

多研究一下正则 这东西烧脑




欢迎光临 批处理之家 (http://www.bathome.net/) Powered by Discuz! 7.2