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

时间:2016-03-30 08:48:56

标签: regex perl

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

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

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

因此,我需要确定哪个keys %testhash包含[a-zA-Z],但指数符号除外。

有没有办法用优雅的单一正则表达式行检查这一点,而无需削减每个值并单独分析?

提前致谢! 亚历

编辑:像新手经常发布一个问题并且没有提供必要的示例数据(对不起),所以我将更新哈希

    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 个答案:

答案 0 :(得分: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))是一个否定前瞻声明:如果没有 e,则会出现[+-]\d

如果您只需要检查正则表达式本身的值

$val !~ /[a-df-z]|(?!([+-]\d))/i and $val !~ /(?:E[-+]\d)?.*[a-df-z]/i

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

更新需要向条形e字符串添加预测,但允许e-02个指数。

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

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

答案 1 :(得分:1)

如果

,表达式有效
  • 其字符均不是“E”以外的字母,
  • 每个“E”前面都有一个数字,
  • 每个“E”后跟一个符号和一个数字。

因此 [1] ,如果

,表达式无效
  • 其中一个字符是“E”以外的字母,
  • 有一个“E”,前面没有数字,或
  • “E”后面没有符号和数字。

这给了我们以下内容:

/ [A-DF-Z] | (?<![0-9])E | E(?![+-][0-9]) /ix
  1. 记住,

    • 否定“ A B ”是“(不是 A )或(不是 B ) ”
    • 否定“所有 A ”是“至少有一个不是 A ”,并且
    • 否定“无 A ”是“至少有一个 A ”。

答案 2 :(得分: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或更多,这要归功于[+*\/-])。并且允许空间存在。
但是如果你在语法中添加括号,那么它会变得稍微复杂一点,例如你可能需要一个递归的正则表达式。