使用Regex从该字符串中获取字符串模式
本文关键字:字符串 模式 获取 Regex 使用 | 更新日期: 2023-09-27 18:19:45
我的C#应用程序中有一个字符串,如下所示。
Multiply(Sum(3,5,4), Division(4,5,5), Subtract(7,8,9))
Sum()
、Division()
、Subtract()
是Multiple()
内部不同的方法。
有没有什么方法可以像Sum(3,5,4)
、Division(4,5,5)
、Substract(7,8,9)
和Multiply()
那样使用C#Regex方法分别获得它们?
Sum
、Division
、Substract
和Multiply
是恒定关键词。
如果嵌套是任意深度的,则应该使用Regexp.Matches()
和Regexp.Replace()
之类的东西迭代执行此操作。
复制你的整个字符串。使用([a-zA-Z]+'([0-9, ]*'))(, )?
作为正则表达式。这将匹配所有最低级别的函数调用——调用图的所有叶节点。
调用Regexp.Matches
提取所有匹配项,调用Regexp.Replace
从字符串副本中删除所有匹配项。这将去掉调用图的所有叶节点。再次调用Matches()
和Replace()
以摆脱下一级别的调用,并不断重复,直到字符串副本为空。
您不能用RegExp进行任意嵌套-由于RegExp模型的限制,即使在理论上也不可能。
在这种情况下,您需要的是解析器。手动构建一个非常简单的递归下降语法分析器不需要太多工作,但一旦复杂性变得相当大,就应该切换到语法分析器生成器。我个人最喜欢的是ANTLR,但你还有很多其他选择。
如果在向方法传递参数时不使用其他方法调用,则为是
(如Sum(2, Sum(3,2), 4)
)
在这种情况下,您可以使用以下模式:^'w+'((.*)')$
然后得到作为参数的组1(它是(.*)组)(Sum(3,5,4), Division(4,5,5), Subtract(7,8,9)
),然后对getted组使用此模式来查找所有参数:'w+'(.*')
如果您的Multiply方法可能有其他嵌套方法,regexp将无法帮助您。在这种情况下,您应该计算大括号以查看哪个wher已关闭
C#应该能够通过正则表达式中的递归来实现平衡文本。唯一的问题是,我认为它保留了整个外围比赛。为了进一步解析内部内容(括号之间),需要一个递归函数调用,每次都提取标记。
不过,我同意@dasblinkenlight关于需要一个像样的解析器的观点。正如他所说,复杂性很快就会变得相当大。
下面的正则表达式来自Perl,但对于.Net黑客攻击,其结构应该是相同的
正如您所看到的,regex就像一个seive,因为它遵循了一般形式,但
在Math标记之间只处理逗号和数字,允许其余部分通过。
但是,如果这是你唯一关心的事情,那么它应该起作用。您会注意到,即使您可以将其解析为数据结构(如下所示),但要以内部方式使用该结构,还需要对数据结构进行另一次递归"解析"(尽管更容易)。如果是为了显示或统计目的,那么这不是问题。
扩展的正则表达式:
{
( #1 - Recursion group 1
'b('w+)'s* #2 - Math token
'( # - Open parenth
( #3 - Capture between parenth's
(?: (?> (?: (?!'b'w+'s*'(|')) . )+ ) # - Get all up to next math token or close parenth
| (?1) # - OR, recurse group 1
)* # - Optionally do many times
) # - End capture 3
') # - Close parenth
) # - End recursion group 1
's*(',?) #4 - Capture optional comma ','
| # OR,
# (Here, it is only getting comma and digits, ignoring the rest.
# Comma's ',' are escaped to make them standout)
's*
(?| # - Start branch reset
('d+)'s*(',?) #5,6 - Digits then optional comma ','
| (?<=',)()'s*(',|'s*$) #5,6 - Comma behind. No digit then, comma or end
) # - End branch reset
}xs; # Options: expanded, single-line
这里有一个Perl的快速原型(比C#更容易):
use Data::Dumper;
#//
my $regex = qr{('b('w+)'s*'(((?:(?>(?:(?!'b'w+'s*'(|')).)+)|(?1))*)'))'s*(',?)|'s*(?|('d+)'s*(',?)|(?<=',)()'s*(',|'s*$))}s;
#//
my $sample = ', asdf Multiply(9, 4, 3, hello, _Sum(3,5,4,) , Division(4, Sum(3,5,4), 5), ,, Subtract(7,8,9))';
print_math_toks( 0, $sample );
my @array;
store_math_toks( 0, $sample, '@array );
print Dumper('@array);
#//
sub print_math_toks
{
my ($cnt, $segment) = @_;
while ($segment =~ /$regex/g )
{
if (defined $5) {
next if $cnt < 1;
print "'t"x($cnt+1), "$5$6'n";
}
else {
++$cnt;
print "'t"x$cnt, "$2('n";
my $post = $4;
$cnt = print_math_toks( $cnt, $3 );
print "'t"x$cnt, ")$post'n";
--$cnt;
}
}
return $cnt;
}
sub store_math_toks
{
my ($cnt, $segment, $ary) = @_;
while ($segment =~ /$regex/g )
{
if (defined $5) {
next if $cnt < 1;
if (length $5) {
push (@$ary, $5);
}
else {
push (@$ary, '');
}
}
else {
++$cnt;
my %hash;
$hash{$2} = [];
push (@$ary, '%hash);
$cnt = store_math_toks( $cnt, $3, $hash{$2} );
--$cnt;
}
}
return $cnt;
}
输出:
Multiply(
9,
4,
3,
_Sum(
3,
5,
4,
),
Division(
4,
Sum(
3,
5,
4
),
5
),
,
,
Subtract(
7,
8,
9
)
)
$VAR1 = [
{
'Multiply' => [
'9',
'4',
'3',
{
'_Sum' => [
'3',
'5',
'4',
''
]
},
{
'Division' => [
'4',
{
'Sum' => [
'3',
'5',
'4'
]
},
'5'
]
},
'',
'',
{
'Subtract' => [
'7',
'8',
'9'
]
}
]
}
];