PHP解析Telnet返回ONT数据写入MySQL字段拆分异常求助
Telnet返回固定列宽ONT表解析失败致MySQL写入异常修复
问题场景
开发小型项目时需获取Telnet命令返回结果并解析,将ONT设备信息更新至MySQL数据表,目前出现数据无法正确写入表、更新失效问题,具体信息如下:
- 数据源:执行
show ont info 1 all命令后,Telnet返回固定列宽格式的ONT信息表格,包含F/S、P、ONT ID、MAC、Control flag、Run state、Config state、Match state、Desc共9个字段,对应多条ONT设备记录。 - 原有实现逻辑(技术术语、代码标识符保持原样):
- 调试缓存逻辑:
$debug=true时优先读取本地/tmp/onu_data_auth-info.log缓存内容作为响应,无缓存则调用$telnet->DoCommand($show_pon, $poninfo_resposta)获取响应并写入缓存;非调试模式直接调用Telnet方法获取响应。 - 响应预处理:通过
preg_split('/\r?\n/', trim($poninfo_resposta))按行分割响应内容,初始化$headers数组存储各表头偏移量,创建PDO实例连接本地MySQL数据库。 - 遍历解析逻辑:逐行遍历响应内容,若匹配到含F/S和Match state的表头行,则通过stripos计算每个表头在该行的起始偏移量存入
$headers;若匹配到含0/0的数据行,则根据$headers存储的偏移量通过substr截取对应字段值存入$fields1数组,生成ont_onu表的INSERT预处理SQL语句,绑定$fields1参数执行写入。
- 调试缓存逻辑:
故障表现
字段截取逻辑失效,解析得到的$fields1数组仅最后一个元素存储了整行未拆分的原始内容,未得到按字段拆分的预期数组结构,导致SQL无法正确执行、数据无法入库。
根因分析
固定列宽表格解析逻辑存在两个核心缺陷:
- 仅记录了每个字段的起始偏移位置,未计算每个字段对应的截取长度,substr调用时未传length参数时默认截取到行尾,最终只有最后一次赋值的字段值为整行内容,覆盖了之前的截取结果。
- 未对匹配到的表头偏移量按出现顺序做排序,若stripos匹配表头的顺序和表格列的实际展示顺序不一致,会直接导致后续截取错位。
修复方案
- 表头解析阶段补充顺序校验
匹配到表头行后,按列的固定顺序匹配每个表头的位置,再按偏移量从小到大排序,保证字段顺序和表格展示顺序一致,参考代码:
// 表头行匹配成功后执行 $headerDefine = ['F/S', 'P', 'ONT ID', 'MAC', 'Control flag', 'Run state', 'Config state', 'Match state', 'Desc']; $headers = []; foreach ($headerDefine as $colName) { $pos = stripos($headerLine, $colName); if ($pos !== false) { $headers[$colName] = $pos; } } // 按偏移量升序重排,避免顺序错乱 asort($headers); $colOffsets = array_values($headers);
- 数据行截取阶段补全长度计算
逐字段截取时,当前字段的截取长度为下一个字段的起始偏移量减去当前字段的起始偏移量,最后一个字段直接截取到行尾,截取后统一去除首尾空白字符,参考代码:
// 0/0开头的数据行匹配成功后执行 $fields1 = []; $totalCol = count($colOffsets); for ($i = 0; $i < $totalCol; $i++) { $startPos = $colOffsets[$i]; // 非最后一列,用下一列偏移量减当前偏移量得到列宽 if (isset($colOffsets[$i+1])) { $colLen = $colOffsets[$i+1] - $startPos; $fieldVal = substr($dataLine, $startPos, $colLen); } else { // 最后一列直接截到行尾 $fieldVal = substr($dataLine, $startPos); } $fields1[] = trim($fieldVal); }
- 入库前增加完整性校验
执行PDO预处理绑定前,先校验$fields1数组长度是否为9,长度不符则跳过该行并记录错误日志,避免参数数量不匹配导致SQL执行失败。
优化建议
- 响应预处理阶段增加不可见控制字符过滤,通过
preg_replace('/[\x00-\x1F\x7F]/', '', $line)清除Telnet返回的换行、退格等特殊控制符,避免偏移量计算偏差。 - 缓存文件读写统一使用UTF-8编码,避免编码不一致导致中文字段长度计算错误。
内容的提问来源于stack exchange,提问作者0p3nS0urc3 2015
相关产品推荐
相关产品推荐

