用std::getline逐行读取NMEA日志,跳过空行、注释行和空白,再用std::string_view按逗号安全提取字段;优先用std::from_chars解析数字,手动处理ddmm.mmmm格式的经纬度,严格校验字段存在性与有效性。

直接读取 NMEA 日志文件,用 std::string 按行切分 + std::string_view 提取字段最稳,别碰正则、别手写状态机。
为什么不能用 std::getline 读完就 split?
NMEA 行末可能带 \r\n 或仅 \n,有些日志还混着空行、注释行(以 # 开头)甚至校验失败的脏数据。直接 split 会把空字段、校验位、换行符全塞进 vector,后续解析容易越界或误判。
- 先用
std::getline读整行,再用line.find_first_not_of(" \t\r\n")跳过首尾空白 - 跳过空行和
#开头的注释行:if (line.empty() || line[0] == '#') continue; - NMEA 标准以
$开头,但有些日志导出时去掉了,得兼容无$的纯逗号分隔行
std::string_view 提取字段比 substr 更快且安全
每次 substr 都分配新字符串,而 NMEA 字段短、重复多(比如反复取 GPGGA、GPVTG),用 std::string_view 只存指针+长度,避免拷贝开销,也杜绝了 substr 越界抛异常的风险。
- 定位第
n个字段:写个简单循环找第n个逗号位置,别依赖第三方 split 库 - 提取时检查逗号是否存在:
auto start = line.find(',', pos) + 1;前先判断start != std::string::npos - 校验字段有效性:NMEA 第 0 字段是语句标识(如
GPGGA),长度固定为 5,用sv.length() == 5快速过滤乱码
解析 GPGGA 时,纬度/经度字段格式最容易出错
NMEA 的 lat/lon 不是小数度,而是 ddmm.mmmm 格式(度分),且方向字符(N/S/E/W)在单独字段里。直接转 double 会错得离谱。
- 纬度字段示例:
"4807.5123"+"N"→ 实际是48 + 7.5123/60 = 48.125205° - 必须先按小数点分割:
auto dot = sv.find('.');,再分别取整数部分(前两位是度)、小数部分(剩余是分) - 方向字符字段为空或非法时(如
""或"X"),应设默认值或跳过整条记录,别硬转 - 注意:有些设备输出
0000.0000表示无效定位,需检查数值是否全零
性能关键点:别在循环里新建 std::stringstream 或调用 std::stod
每秒上百条 NMEA 记录时,频繁构造/析构 std::stringstream 是明显瓶颈;std::stod 虽方便但内部做 locale 判断和异常检查,对纯 ASCII 数字浪费资源。
- 用
std::from_chars(C++17)替代:double val; std::from_chars(sv.data(), sv.data() + sv.size(), val); - 如果必须兼容 C++11,手写简易 ASCII 浮点解析(只处理
ddd.dddd类型,不支持科学计数法)比stod快 3–5 倍 - 时间戳字段(如
hhmmss.sss)可拆成三段整数解析,避免浮点运算误差
真正麻烦的不是读取,而是字段缺失和格式漂移——同一台设备固件升级后可能突然多一个字段,或把 W 改成 W.。解析逻辑里每个 sv.substr() 都得配 if (pos != std::string::npos),宁可多几行判断,别信“它一定有”。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











