【问题标题】:Php function to expand chemical equations扩展化学方程式的php函数
【发布时间】:2014-01-07 08:52:18
【问题描述】:

目前我正在尝试在 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;

}

示例:

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"

我从一周前开始尝试解决这个问题,但我仍然没有设法解决它。

【问题讨论】:

  • 可能是贪心匹配。试试/iU 而不是/i
  • 感谢您的评论。我测试了将 /iU 添加到正则表达式,但这并没有解决问题。我可能需要重新编写代码......
  • 我强烈建议在这里使用堆栈解析器而不是正则表达式。 every ( 推入一个新堆栈,every ) + number 弹出一个堆栈,将整个内容相乘,然后将结果元素重新推入。

标签: php regex string function


【解决方案1】:

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

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

也许更复杂的正则表达式来自expand 函数:

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

使用负前瞻来确保我们首先处理内部括号中的项目, 还首先匹配“乘数”类型的项目,然后再匹配更简单的括号项目。

<?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();

【讨论】:

  • 好的,然后 +1 和加星标的问题。感谢您的信息。
  • 答案解释得很好。谢谢你。对我有很大帮助。
猜你喜欢
  • 2020-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-24
相关资源
最近更新 更多