代码之家  ›  专栏  ›  技术社区  ›  D. Petrov

将regex和字符串分析结合起来,指定字符串输入验证所需的模式

  •  1
  • D. Petrov  · 技术社区  · 7 年前

    首先,我应该为我可能是新手的问题道歉,但作为一个关于regex的新手,我不知道如何完成这个相对复杂的任务。我需要的是为字符串输入指定一个验证模式,并对该模式的各个部分执行单独的检查。因此,让我们从任务本身开始。我正在使用laravel 5.4上的php7.0(这真的不会有任何区别),我需要为字符串输入生成一个匹配模式,该模式如下:

    header1: expression1; header2: expression2; header3: expression3 //etc...
    

    这里我需要检查每个标题是否存在,以及它是否存在于可用标题的特殊验证列表中。所以我需要提取每个标题。

    此外,表达式构建如下

    expression1 = (a1 + a2)*(a3-a1)
    expression2 = b1*(b2 - b3)/b4
    //etc...
    

    关键是,每个表达式都包含一些数字参数,这些参数应构成有效的算术计算。这些参数还应该包含在可用参数占位符的特殊列表中,所以我也需要检查它们。那么,有没有一种简单有效的方法(在纯php中使用regex和字符串分析)来指定这种严格的结构,或者我应该一步一步地展开并尝试捕捉?

    最佳解决方案是一个速记逻辑(或regex表达式?)类似于:

    $value->match("^n(header: expression)")
    ->delimitedBy(';')
    ->where(in_array($header, $allowed_headers))
    ->where(strtr($expression, array_fill_keys($available_param_placeholders, 0))->isValidArithmeticExpression())
    

    我希望你能遵循我的逻辑。上面的代码应为:匹配N个重复的模式“header:expression”,由“;”分隔,其中,“header”(假定$header是其值)位于数组中,“expression”(假定$expression是其值)在所有可用的参数占位符都被0替换时形成有效的算术表达式。仅此而已。该严格模式的每个偏差都应返回false。

    作为另一种选择,我目前正在考虑这样的事情,首先用主分隔符(分号)分解字符串,然后分别分析每个部分。因此,我必须检查是否存在冒号,冒号左侧的所有内容是否匹配有效的标头名称,以及当列表中的所有参数名称都被随机值替换时,列右侧的所有内容是否形成有效的算术表达式(如0,只是为了检查代码是否执行,我也不知道如何执行)。无论如何,这种方式似乎有点过头了,我相信应该有一种更平滑的方式来指定所需的模式。

    我希望我已经解释得足够好了,如果我在解释我的问题时弄得一团糟,那我很抱歉。提前感谢您的每一条建议/帮助!非常感谢!

    3 回复  |  直到 7 年前
        1
  •  1
  •   mickmackusa Tom Green    7 年前

    使用 eval() 必须始终是计划Z。根据我对输入字符串的理解,此方法可以充分验证标头和表达式(如果不是,我认为它应该充分清理字符串以进行算术解析)。我不使用Laravel编写代码,所以如果可以将其转换为Laravel语法,我将把这项工作留给您。

    代码:( Demo )

    $test = "header1: (a1 + a2)*(a3-a1); header2: b1*(b2 - b3)/b4; header3: c1 * (((c2); header4: ((a1 * (a2 - b1))/(a3-a1))+b2";
    $allowed_headers=['header1','header3','header4'];
    
    $pairs=explode('; ',$test);
    foreach($pairs as $pair){
        list($header,$expression)=explode(': ',$pair,2);
        if(!in_array($header,$allowed_headers)){
            echo "$header is not permitted.";
        }elseif(!preg_match('~^((?:[-+*/ ]+|[a-z]\d+|\((?1)\))*)$~',$expression)){  // based on https://stackoverflow.com/a/562729/2943403
            echo "Invalid expression @ $header: $expression";
        }else{
            echo "$header passed.";
        }
        echo "\n---\n";
    }
    

    输出:

    header1 passed.
    ---
    header2 is not permitted.
    ---
    Invalid expression @ header3: c1 * (((c2)
    ---
    header4 passed.
    ---
    

    我承认上述模式会匹配 (+ )( +) 所以这不是 乳房 最佳图案。因此,您的问题可能是 评估() . 尽管您可能想考虑/研究一些github创建/插件/解析器,它们可以首先解析/标记算术表达式。

    也许:

    任何 $pair 超过了 if 以及 elseif 可以在 else .

    我会给你一个关于一些一般处理的开头/暗示,但我会避免给出任何直接的指示,以避免某些批评人士的愤怒。

    }else{
        // replace all variables with 0
        //$expression=preg_replace('/[a-z]\d+/','0',$expression);
        // or replace each unique variable with a whole number
        $expression=preg_match_all('/[a-z]\d+/',$expression,$out)?strtr($expression,array_flip($out[0])):$expression;  // variables become incremented whole numbers
        // ... from here use $expression with eval() in a style/intent of your choosing.
        // ... set a battery of try and catch statements to handle unsavory outcomes.
        // https://www.sitepoint.com/a-crash-course-of-changes-to-exception-handling-in-php-7/
    }
    
        2
  •  0
  •   Will Hines    7 年前
    $test = "header1: (a1 + a2)*(a3-a1); header2: b1*(b2 - b3)/b4; header3: expression3";
    $pairs = explode(';', $test);
    $headers = [];
    $expressions = [];
    foreach ($pairs as $p) {
        $he = explode(':', $p);
        $headers[] = trim($he[0]);
        $expressions[] = trim($he[1]);
    }
    foreach ($headers as $h) {
        if (!in_array($h, $allowed_headers)) {
            return false;
        }
    }
    
    foreach ($expressions as $e) {
        preg_match_all('/[a-z0-9]+/', $e, $matches);
        foreach ($matches as $m) {
            if (param_fails($m)) {
                echo "Expression $e contains forbidden param $m.";
            }
        }
    }
    
        3
  •  0
  •   D. Petrov    7 年前

    Regex似乎并不像我发帖时想的那么复杂,所以我自己就成功地实现了这个模式的完整形式,最初的开头归功于@mickmackusa。我最后想到的是,在这里 regex101 自身: https://regex101.com/r/UHMrqL/1 最初的问题描述了它所基于的逻辑。唯一缺少的是验证头的值和参数的名称,但之后很容易与匹配 preg_match_all 并使用纯php检查进行验证。再次感谢您的关注和帮助!:)