如何将含操作指令的字符串转换为指定格式PHP数组?
解决PHP解析含括号的节点操作字符串为数组的问题
问题场景
原始字符串格式(包含HTML标签包裹的节点操作列表):
<p>[MoveNode(node='/html/head/meta[6]', target='/html/head[1]', position=22), MoveNode(node='/html/body/div[1]/main/div[1]/div/div/div/div/div[2]/ul/li[1]/a[1]', target='/html/head[1]', position=15), RenameNode(node='/html/head/a[1]', tag='meta'), InsertAttrib(node='/html/head/meta[6]', name='content', value='text text text text (text text), text'), InsertAttrib(node='/html/head/meta[6]', name='name', value='description'), DeleteAttrib(node='/html/head/meta[6]', name='href'), DeleteAttrib(node='/html/head/meta[6]', name='title'), UpdateTextIn(node='/html/head/meta[6]', text=None), MoveNode(node='/html/body/div[1]/main/footer/ul/li[2]/a[1]', target='/html/head[1]', position=16), RenameNode(node='/html/head/a[1]', tag='meta'), DeleteNode(node='/html/body/div[1]/main/div[1]/div/header/div/div/div[2]/div/h6[1]'), DeleteNode(node='/html/body/div[1]/main/div[1]/div/header/div/div/div[2]/div[1]')]</p>
需要转换为带索引后缀的PHP数组格式:
Array ( [MoveNode_1] => Array ( [node] => /html/head/meta[6] [target] => /html/head[1] [position] => 22 ) [InsertAttrib_4] => Array ( [node] => /html/head/meta[6] [name] => content [value] => text text text text (text text), text ) // ... 其他项省略 )
原代码的问题
原代码使用explode("), ")分割字符串,当操作值中包含括号或逗号时(比如InsertAttrib的value字段),会错误地提前分割字符串,导致后续解析的键值对不完整。同时用parse_str直接替换单引号和逗号的方式,无法保留值中的特殊字符,也会破坏带逗号的字段内容。
解决方案:正则匹配精准解析
使用正则表达式精准匹配每个完整的操作项,以及内部的键值对,避免特殊字符干扰分割逻辑。完整代码如下:
$str = '<p>[MoveNode(node=\'/html/head/meta[6]\', target=\'/html/head[1]\', position=22), MoveNode(node=\'/html/body/div[1]/main/div[1]/div/div/div/div/div[2]/ul/li[1]/a[1]\', target=\'/html/head[1]\', position=15), RenameNode(node=\'/html/head/a[1]\', tag=\'meta\'), InsertAttrib(node=\'/html/head/meta[6]\', name=\'content\', value=\'text text text text (text text), text\'), InsertAttrib(node=\'/html/head/meta[6]\', name=\'name\', value=\'description\'), DeleteAttrib(node=\'/html/head/meta[6]\', name=\'href\'), DeleteAttrib(node=\'/html/head/meta[6]\', name=\'title\'), UpdateTextIn(node=\'/html/head/meta[6]\', text=None), MoveNode(node=\'/html/body/div[1]/main/footer/ul/li[2]/a[1]\', target=\'/html/head[1]\', position=16), RenameNode(node=\'/html/head/a[1]\', tag=\'meta\'), DeleteNode(node=\'/html/body/div[1]/main/div[1]/div/header/div/div/div[2]/div/h6[1]\'), DeleteNode(node=\'/html/body/div[1]/main/div[1]/div/header/div/div/div[2]/div[1]\')]</p>'; // 清理字符串前后的HTML标签和外层括号 $cleanStr = trim($str, '<p>[]</p>'); $result = []; $index = 1; // 匹配每个完整的操作项:匹配ActionName(...)结构,忽略内部括号/逗号 preg_match_all('/(\w+)\((.*?)\)(?=\s*,|\s*$)/s', $cleanStr, $matches, PREG_SET_ORDER); foreach ($matches as $match) { $actionName = $match[1]; $paramsStr = $match[2]; $params = []; // 匹配每个键值对:支持key='带特殊字符的值' 或 key=None格式 preg_match_all('/(\w+)\s*=\s*(?:'(.*?)'|(\w+))/', $paramsStr, $paramMatches, PREG_SET_ORDER); foreach ($paramMatches as $pm) { $key = $pm[1]; // 优先取单引号包裹的值,无引号时取字面量(如None) $value = !empty($pm[2]) ? $pm[2] : $pm[3]; // 将None转换为PHP的null if ($value === 'None') { $value = null; } $params[$key] = $value; } // 组装带索引后缀的键名 $result["{$actionName}_{$index}"] = $params; $index++; } print_r($result);
代码说明
- 字符串清理:先移除原始字符串前后的
<p>标签和外层方括号,得到纯净的操作列表字符串。 - 操作项匹配:第一个正则
/(\w+)\((.*?)\)(?=\s*,|\s*$)/s精准匹配每个ActionName(...)单元,利用正向预查确保只在操作项末尾的逗号或字符串结尾处分割,避免值内的特殊字符干扰。 - 键值对解析:第二个正则
/(\w+)\s*=\s*(?:'(.*?)'|(\w+))/分别捕获单引号包裹的带特殊字符的值,以及无引号的字面量(如None),确保每个键值对正确解析。 - 结果组装:为每个操作项添加递增的索引后缀,生成目标格式的数组。
内容的提问来源于stack exchange,提问作者User
相关产品推荐
相关产品推荐

