目前我正在尝试在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"
我试图解决这个问题,因为一周前我还没有设法解决它。
答案 0 :(得分:2)
不是堆叠的解析器,而是简单的重复嵌套循环。
使用preg_replace_callback
可以更轻松地进行字符串替换。
可能更复杂的RegEx来自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();