Perl:如何检查数学表达式中是否包含字母数字部分

3

我有一个哈希表,其中包含一些数学表达式,它看起来像这样:

    my %testhash = ("ABC" => "0.05 + 1 + foo",
                    "DEF" => "2E+5 -3",
                    "GHJ" => "2E+5 -3 * bar);

在我的代码中,我想评估每个%testhash值,但只有有效值(在本例中只有"DEF",因为foobar未定义)

因此,我需要确定哪些keys %testhash包含[a-zA-Z],除指数符号外。
有没有一种优雅的方式可以使用单个正则表达式行来检查这个问题,而不必切割每个值并单独分析?
提前感谢! Alex 编辑: 像往常一样,新手发布问题时没有提供必要的示例数据(对此表示抱歉),所以我将更新哈希表
    my %testhash = ("invalid1" => "0.05 + 1 + foo",
                    "invalid2" => "2E+5 -3 * bar",
                    "valid1"   => "1.0e-03 + ( 42)",
                    "valid2"   => "((3.0) / 3) * 5",
                    "valid3"   => "2E+5 + 1");
3个回答

1

我不知道如何在一条语句中处理整个哈希表,因为你需要按值过滤但同时排除键。这只会按请求排除 [a-zA-Z]

my %valid_expr;
while ( my ($key, $val) = each %testhash ) {
    $valid_expr{$key} = $val  if not (
        $val =~ / [a-df-z] | e(?!([+-]\d)) /xi or 
        $val =~ / (?:E[-+]\d)? .* [a-df-z] /xi
    );
}

正则表达式:禁止在一个可选的 E[+-]\d+ -- E 后面跟着任何字母(或字母前面有任何字母),或者在其后面跟着任何字母(或字母后面有任何字母)。.* 之间允许数字/小数点,运算符。 e(?!([+-]\d)) 是一个负向先行断言:如果不是跟随着 [+-]\d,则字母 e
如果您只需要检查值,则正则表达式本身为:
$val !~ /[a-df-z]|(?!([+-]\d))/i and $val !~ /(?:E[-+]\d)?.*[a-df-z]/i

更新 添加A-Z到原始帖子中禁止E,已更正。 更改为/i以允许e

更新 需要添加前瞻以查找栏e字符串,但允许e-02指数。

这将正确评估所有示例,以及其他一些示例。

然而,它最终收敛到ikegami的解决方案。


1

一个表达式是有效的,如果

  • 它的所有字符都不是除了"E"之外的字母,
  • 每个"E"前面都有一个数字,
  • 每个"E"后面都跟着一个符号和一个数字。

因此[1],如果

  • 它的其中一个字符是除了"E"之外的字母,
  • 有一个"E"没有被数字前导,或者
  • 有一个"E"没有跟着符号和数字。

这给我们带来了以下结果:

/ [A-DF-Z] | (?<![0-9])E | E(?![+-][0-9]) /ix

  1. 记住,

    • "AB" 的否定是 "(不是 A) 或者 (不是 B)",
    • "所有都是 A" 的否定是 "至少有一个不是 A",以及
    • "没有一个是 A" 的否定是 "至少有一个是 A"。

对于未来的读者,您能否简要概述一下如何匹配所需输入(而不是不需要的输入)? - Mr. Llama

0

编辑:如评论中所指出的,不要使用eval解决方案,因为除了数学表达式之外的其他东西也可以被评估和接受(实际上,任何Perl代码都可以执行,这是一个相当大的问题)。如果您仍然想使用eval,则需要添加use strict
因此,最好的方法可能是使用正则表达式。顺便说一下,如果您想允许\n、\t等而不仅仅是常规空格,可以将所有的/ */替换为/\s*/


如果您想进行评估,可能需要使用某种解析器或eval函数,它们都可以告诉您在刚刚读取的数学表达式中是否存在错误。
例如,您可以执行以下操作:

while (my ($name, $expr) = each %testhash) {
    if (defined(my $val = eval($expr))) {
        say "$name => $expr => $val"
    } else {
        print "$name invalide => $@"
    }
}

它还可以帮助您检测语法错误(例如,3 + 5 2将不被接受,这可能是一个好消息)。


检测错误的另一种方法是使用正则表达式来检查表达式是否有效,而不是无效:

while (my ($name, $expr) = each %testhash) {
    if ($expr !~ m/^ *(\d+(\.\d+)?(E([+-])\d+)?)( *[+*\/-] *(\d+(\.\d+)?(E([+-])\d+)?))* *$/) {
        say "$name: invalide expr";
    }
}

正则表达式搜索数字(可能带有小数部分或指数)的部分为:(\d+(\.\d+)?(E([+-])\d+)?)。然后它查找一个操作符([+*\/-])后跟一个数字的列表(0个或多个,感谢*)。在应该出现的地方允许空格。
但是,如果您将括号添加到语法中,则会变得稍微复杂一些,您可能需要使用递归正则表达式。


我认为在未先分析表达式时(例如$val包含系统命令),使用eval是不安全的。除此之外,我喜欢这个解决方案,因为错误的语法也会被检测到。 - Alex
你的第一个解决方案表示,除非使用了 use strict;,否则 OP 的三个解决方案都是有效的。它表示即使使用了 use strict;2 * time 也是有效的。 - ikegami
为什么要使用/ */而不是\s* / - ikegami
是的,eval可能不是最好的选择。回答已经编辑过了。@ikegami:关于/ * /和/ \s * /:我想\s更有意义,但当我写它时,我认为不允许\n或\t可能有意义。 - Dada

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接