有没有大神帮忙写个正则表达式匹配字符串中的运算式

2020-12-18 10:42:40 +08:00
 klzhoushuai

例:a1=1&&a2=2&&a3=3&&(a4=1||a5=2||(a6=3&&a7=4))
匹配结果:
array:7 [
0 => "a1=1"
1 => "a2=2"
2 => "a3=3"
3 => "a4=1"
4 => "a5=2"
5 => "a6=3"
6 => "a7=4"
]
例:a1==1 && a2==3 && ((a4a5+(a6+a7)/3)>10 || (a1 /((a2/100)(a2/100))) >= 28 || (a3*((a1)+(a4-a5)+(a2))>4) ) && a10==6 || a9==9 && (a3>4 || a5 <6)
匹配结果:
array:9 [
0 => "a1==1"
1 => "a2==3"
2 => "a4a5+(a6+a7)/3>10"
3 => "a1/((a2/100)
(a2/100))>=28"
4 => "a3*((a1)+(a4-a5)+(a2))>4"
5 => "a10==6"
6 => "a9==9"
7 => "a3>4"
8 => "a5<6"
]

1519 次点击
所在节点    问与答
18 条回复
SmallTeddy
2020-12-18 11:10:24 +08:00
你这个 split('&&')拆分开就行吧
Cbdy
2020-12-18 11:41:40 +08:00
用什么编程语言?我用 Perl 写个你能用吗?
conanforever22
2020-12-18 13:10:05 +08:00
如果是 shell, 用`tr`把'|&()'替换成'\n\n '然后 trim 掉前后空格就行了

echo 'a1=1&&a2=2&&a3=3&&(a4=1||a5=2||(a6=3&&a7=4))' | tr '|&()' '\n\n '
klzhoushuai
2020-12-18 13:10:58 +08:00
@SmallTeddy
((a4a5+(a6+a7)/3)>10 || (a1 /((a2/100)(a2/100)))
向这种情况就不好拆分了
cheese
2020-12-18 13:14:41 +08:00
@klzhoushuai #4 两次循环就好了,先判断&&再循环合并||。不需要正则,这个正则做还挺麻烦的
lululau
2020-12-18 13:23:28 +08:00
这个问题和正则没关系,请自行搜索“parser for mathematical expression”
SmallTeddy
2020-12-18 13:34:56 +08:00
@lululau 我觉得也和正则没关系
klzhoushuai
2020-12-18 14:02:46 +08:00
/**
* 拆分公式
* @param $formula
* @return array
*/
public function splitFormula($formula)
{
$formula = $t = str_replace(' ', '', $formula);
$formula = str_replace(['&&', '||'], [';', ';'], $formula);

$result = explode(';', $formula);

foreach ($result as $key => $value) {
while (true) {
$left = substr_count($value, '(');
$right = substr_count($value, ')');

if ($left === $right) break;

if ($left > $right) {
$value = substr_replace($value, '', strpos($value, '('), 1);
} else {
$value = substr_replace($value, '', strrpos($value, ')'), 1);
}
}
$result[$key] = $value;
}
return $result;
}
klzhoushuai
2020-12-18 14:03:38 +08:00
@klzhoushuai 用了一个比较笨的方法写的
rrfeng
2020-12-18 14:04:53 +08:00
这个难点在括号。写个 parser 吧。
ipwx
2020-12-18 14:07:00 +08:00
1. 没看懂楼主想干嘛。
2. 递归文法的解析我记得超出了 regex 的范畴。
zy445566
2020-12-18 14:29:43 +08:00
这不是正则的范畴了,这是语法解析
gIQgxDI09Jflpdc2
2020-12-18 15:53:17 +08:00
@klzhoushuai
分两步。
1 用正则取出 &&或者|| 符号中间的内容。
2 利用字符串替换多余的括号。

PHP 写法如下:
```
// 两个字符串例子
$str1 = 'a1=1&&a2=2&&a3=3&&(a4=1||a5=2||(a6=3&&a7=4))';
$str2 = 'a1==1 && a2==3 && ((a4a5+(a6+a7)/3)>10 || (a1 /((a2/100)(a2/100))) >= 28 || (a3*((a1)+(a4-a5)+(a2))>4) ) && a10==6 || a9==9 && (a3>4 || a5 <6)';

preg_match_all('/[^&|^\|]+/', $str1, $match);//提取 &&或者||符号中间的内容。
if (!empty($match[0])) {
foreach ($match[0] as &$value) {
$value = trim(removeBrackets($value));//移除多余的括号
}
}

var_dump($match);


/**
* 移除多余的括号
* @param $string
* @return string|string[]|null
*/
function removeBrackets($string)
{
// 左括号 右括号 默认 0 个
$leftBracket = $rightBracket = 0;

// 计算括号个数
for($i=0;$i<strlen($string);$i++){
if ($string[$i] == '(') {
$leftBracket++;
} elseif ($string[$i] == ')') {
$rightBracket++;
}
}
$abs = abs($leftBracket - $rightBracket);//括号个数的差值

if ($abs == 0) {
return $string;
}

if ($leftBracket > $rightBracket) {// 如果 左括号多,移除多余的
$string = preg_replace('/\(/', '', $string, $abs);
} elseif ($leftBracket < $rightBracket) {// 如果 右括号多,移除多余的
$string = preg_replace('/\)/', '', $string, $abs);
}
return $string;
}
```
两个例子转换结果如下:
```
[
"a1=1",
"a2=2",
"a3=3",
"a4=1",
"a5=2",
"a6=3",
"a7=4"
]
第二个和你期待的有一点点不一样,但是我觉得应该也符合你预期。
[
"a1==1",
"a2==3",
"(a4a5+(a6+a7)/3)>10",
"(a1 /((a2/100)(a2/100))) >= 28",
"(a3*((a1+(a4-a5)+(a2))>4) )",
"a10==6",
"a9==9",
"a3>4",
"a5 <6"
]
```
gIQgxDI09Jflpdc2
2020-12-18 15:54:32 +08:00
为什么我的 markdown 语法没生效!排版全乱了,难受。
谁能告诉我为什么吗?
gIQgxDI09Jflpdc2
2020-12-18 15:59:43 +08:00
原来回复还不支持 markdown 。Fine~
gIQgxDI09Jflpdc2
2020-12-18 16:19:28 +08:00
klzhoushuai
2020-12-18 17:15:27 +08:00
@alex2019
好像有点问题,如果字符串为:a1=1&&a2=2&&a3=3&&(a4=1||a5=2||(a6=3&&a7=4 &&((a8+a9)/2>50 || a10/((a1+a2)*10)>20)))
解析出来的最后的一个表达式就有问题了吧:a10/((a1+a2*10>20)) ,正确的结果应该是:a10/((a1+a2)*10)>20
用 $string = preg_replace('/\)/', '', $string, $abs); 匹配时这个是从左到右匹配的
gIQgxDI09Jflpdc2
2020-12-18 17:43:23 +08:00
@klzhoushuai 那你就把字符串反转之后。再调用 [去掉括号] 方法。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/736669

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX