2. 处理注释和空白
任务目标
扩展手写G代码解析器,使其能够正确处理G代码行中的注释与空白字符。注释以分号;开头直至行尾;空白包括空格与制表符。解析器需在词法分析阶段剥离注释与空白,输出干净的命令参数序列,为后续指令分发做准备。
实现方案
在现有解析器基础上增加一个cleanLine()预处理函数:先截断分号注释,再按空白字符切分剩余部分,返回参数数组。该函数不修改原始行,保持解析器无状态。核心逻辑约20行,无外部依赖。
关键完整代码
// gcode_parser.h
#ifndef GCODE_PARSER_H
#define GCODE_PARSER_H
#include <string>
#include <vector>
// 去除注释与多余空白,返回参数列表(不含空串)
std::vector<std::string> cleanLine(const std::string& raw);
// 判断字符串是否为合法G代码指令(如 "G01"、"M03")
bool isCommand(const std::string& );
#endif // GCODE_PARSER_H
// gcode_parser.cpp
#include "gcode_parser.h"
#include <cctype>
std::vector<std::string> cleanLine(const std::string& raw) {
std::vector<std::string> tokens;
std::string current;
for (size_t i = 0; i < raw.size(); ++i) {
char c = raw[i];
// 注释开始:直接终止处理
if (c == ';') break;
// 空白(空格/制表符):切分
if (c == ' ' || c == '\t') {
if (!current.empty()) {
tokens.push_back(current);
current.clear();
}
continue;
}
// 普通字符:累积
current += c;
}
// 收尾最后一个
if (!current.empty()) {
tokens.push_back(current);
}
return tokens;
}
bool isCommand(const std::string& ) {
if (.size() < 2) return false;
char letter = [0];
if (letter != 'G' && letter != 'M') return false;
for (size_t i = 1; i < .size(); ++i) {
if (!std::isdigit(static_cast<unsigned char>([i]))) return false;
}
return true;
}
// main.cpp —— 最小验证入口
#include "gcode_parser.h"
#include <iostream>
int main() {
std::string lines[] = {
"G01 X10 Y20 F300 ; 直线切削",
" M03 S1000\t; 主轴正转",
"G00 X0 Y0",
"; 整行注释,应被完全忽略",
"G28 ; 回原点"
};
for (const auto& raw : lines) {
auto tokens = cleanLine(raw);
std::cout << "原始: [" << raw << "]\n";
std::cout << "清洗: ";
for (const auto& t : tokens) {
std::cout << "[" << t << "] ";
}
std::cout << "\n";
// 验证第一个是否为合法指令
if (!tokens.empty() && isCommand(tokens[0])) {
std::cout << "指令: " << tokens[0] << "\n";
} else {
std::cout << "指令: (无)\n";
}
std::cout << "---\n";
}
return 0;
}
编译与运行
g++ -std=c++11 -Wall -o gcode_test main.cpp gcode_parser.cpp
./gcode_test
验证方法与通过条件
运行程序后,逐行检查输出:
- 第1行应输出
清洗: [G01] [X10] [Y20] [F300],指令为G01——注释; 直线切削被完全剥离; - 第2行开头有2个空格、中间有制表符,应输出
清洗: [M03] [S1000],指令为M03——所有前导/中间空白被压缩; - 第3行无注释无多余空白,输出
[G00] [X0] [Y0]; - 第4行整行为注释,输出
清洗:(空),指令为(无); - 第5行输出
[G28],指令为G28。
若以上5项输出与预期一致,说明注释剥离与空白压缩逻辑正确,可进入后续指令分发阶段。
常见问题
- 注释符出现在字符串中间:G代码标准中分号只作注释起始,不处理引号转义,因此直接截断即可。
- 连续多个空格:代码通过
current.empty()判断避免产生空,无需额外去重逻辑。 - 大小写敏感:G代码指令通常大写,
isCommand()仅识别大写G/M开头;若需兼容小写,可在比较前统一toupper()。