Php函数扩展化学方程式

时间:2013-12-18 23:29:50

标签: php regex string function

目前我正在尝试在php中创建一个函数,用括号扩展化学产品/试剂。我的功能只使用一组括号使用产品/试剂,但在括号内有2个或更多括号或括号的结合处失败。

    function expand_eq($string) {
while(substr_count($string, "(")>=1 and substr_count($string, ")")>=1) {

    $tmpstr01 = preg_replace('/(.*)[(](.+)[)]([0-9]*)(.*)/i', '$1$4', $string);
    $tmpstr02 = preg_replace('/(.*)[(](.+)[)]([0-9]*)(.*)/i', '$2', $string);
    $tmpstr03 = preg_replace('/(.*)[(](.+)[)]([0-9]*)(.*)/i', '$3', $string);

        $tmpstr02 = preg_replace('/([A-Z][a-z]*)([0-9]*)/', '$1$2 ', $tmpstr02);
            if(substr($tmpstr02, -1)===" ") {$tmpstr02 = substr($tmpstr02, 0, -1);} //remove last space
        $tmpstr02 = explode(" ", $tmpstr02);

            for ($j = 0; $j <= count($tmpstr02)-1; $j++) {
                if(preg_match_all('/[A-Z][a-z]*[0-9]+$/', $tmpstr02[$j])) {

                $tmpstr02[$j] = preg_replace('/([A-Z][a-z]*)([0-9]+)$/', '$1', $tmpstr02[$j]) . preg_replace('/([A-Z][a-z]*)([0-9]+)$/', '$2', $tmpstr02[$j]) * $tmpstr03;
                }
                else {
                $tmpstr02[$j] = $tmpstr02[$j] . $tmpstr03;
                }
            }

    $string = $tmpstr01 . implode($tmpstr02);

    }

return $string;

}

Exemples:

echo expand_eq("(NH4)3");
return: "N3H12" - Correct!

echo expand_eq("(Mo3O10)4");
return "Mo12O40" - Correct!

echo expand_eq("(NH4)3(P(Mo3O10)4)");
return "P4Mo0O0N3H12" - Incorrect! Correct value should be: "N3H12PMo12O40"

echo expand_eq("H((O)2)2");
return "HO44" - Incorrect! Correct value should be: "HO4"

我试图解决这个问题,因为一周前我还没有设法解决它。

1 个答案:

答案 0 :(得分:2)

不是堆叠的解析器,而是简单的重复嵌套循环。

使用preg_replace_callback可以更轻松地进行字符串替换。

可能更复杂的RegEx来自expand函数:

\(((?!\()[^()]+)\)(\d+)|\(((?!\()[^()]+)\)

Regular expression visualization

使用负面预测来确保我们首先处理内部括号内的物品, 在更简单的括号内项目之前,首先匹配“乘数”类型的项目。

<?php
class chem_expand {

    protected $multiplier;

    function compute_bracketed_replacement( $groups ) {
        // In-case if invalid input, output "<error>" in string.
        $result = '<error>';

        // If we have "(Chem)Multiplier"
        if ( !empty( $groups[1] ) && !empty( $groups[2] ) ) {
            // Keep multiplier
            $this->multiplier = intval( $groups[2] );

            // Look for "Chem" Or "ChemCount".
            $result           = preg_replace_callback( '/([A-Z][a-z]*)(\d+)?/mx',
                                                       array( $this, 'multiply_digits_replacement' ), $groups[1] );
        } elseif ( !empty( $groups[3] ) ) {
            // Just plain bracketed "(anything here)".
            $result = $groups[3];
        }

        return $result;
    }

    function multiply_digits_replacement( $groups ) {
        // "Chem"
        $result = $groups[1];

        // Assume only one.
        $count = 1;

        // Count.
        if ( !empty( $groups[2] ) ) {
            $count = intval( $groups[2] );
        }

        // Multiply the count out.
        $count = ( $count * $this->multiplier );
        if ( $count > 1 ) {
            // More than one, need the new count in the string.
            $result = $result . $count;
        }

        return $result;
    }

    function test() {
        echo '<p>starting test</p>';
        $test_values = array(
            '(NH4)3'             => 'N3H12',
            '(Mo3O10)4'          => 'Mo12O40',
            '(NH4)3(P(Mo3O10)4)' => 'N3H12PMo12O40',
            'H((O)2)2'           => 'HO4'
        );

        foreach ( $test_values as $input => $expected ) {
            $actual = $this->expand( $input );
            if ( $actual !== $expected ) {
                echo '<p>failure</p>';
                echo '<p>Actual: \"' . $actual . '"</p>';
                echo '<p>Expected: \"' . $expected . '"</p>';

                return;
            }
        }

        echo '<p>success</p>';
    }

    function expand( $subject ) {

        // Expand the inner brackets first.
        // Loop through all the inner "(Formula)Multiplier" first, then simple bracketed "(anything)".
        $output = preg_replace_callback(
            '/\(
            ((?!\()[^()]+)
            \)
            (\d+)
            |
            \(
            ((?!\()[^()]+)
            \)/ix',
            array( $this, 'compute_bracketed_replacement' ), $subject );

        // If we actually changed the content, then call ourselves again and expand the brackets further.
        if ( $output !== $subject ) {
            $output = $this->expand( $output );
        }

        return $output;
    }

}

$instance = new chem_expand();
$instance->test();