如何使用yacc/bison实现非结构化距离数据到公制单位的转换
基于Bison/Yacc实现非结构化距离解析的方案
你需要配合Flex(词法分析器)和Bison(语法分析器)完成解析,核心逻辑是先拆分输入的有效词元,再按规则累加换算为米的数值。
1. 先定义Flex词法规则
首先用Flex过滤无效内容、拆分数字、单位等有效词元,示例规则如下:
%option noyywrap %{ #include "distance.tab.h" %} %% /* 忽略无意义的修饰前缀 */ "about"|"around" { } /* 匹配小数、分数,返回数值token */ [0-9]+(\.[0-9]+)? { yylval.dval = atof(yytext); return NUM; } [0-9]+"/"[0-9]+ { int a,b; sscanf(yytext, "%d/%d", &a, &b); yylval.dval = (double)a / b; return NUM; } /* 匹配各类单位变体,返回单位token */ "m"|"meter"|"meters" { return METER; } "mile"|"miles" { return MILE; } "y"|"yd"|"yard"|"yards" { return YARD; } /* 忽略空白字符 */ [ \t\n]+ { } %%
2. 再定义Bison语法规则
Bison侧负责完成单位换算和数值累加,支持多段距离、带分数等场景,示例规则如下:
%{ #include <stdio.h> static double total_meter = 0; %} /* 指定token值的类型为double */ %define api.value.type {double} %token NUM METER MILE YARD %% /* 距离可由多个数值+单位的组合组成,累加所有换算后的值 */ distance: /* 空输入 */ | distance expr { total_meter += $2; } ; /* 单个数值+单位的换算规则,包含带分数适配 */ expr: NUM METER { $$ = $1 * 1.0; } | NUM MILE { $$ = $1 * 1609.34; } | NUM YARD { $$ = $1 * 0.9144; } | NUM NUM METER { $$ = ($1 + $2) * 1.0; } | NUM NUM MILE { $$ = ($1 + $2) * 1609.34; } | NUM NUM YARD { $$ = ($1 + $2) * 0.9144; } ; %% int yyerror(const char *s) { fprintf(stderr, "解析错误: %s\n", s); return 0; } /* 对外暴露的转换函数,输入原始字符串返回米为单位的数值 */ double convert_to_meter(const char *input) { total_meter = 0; YY_BUFFER_STATE buf = yy_scan_string(input); yyparse(); yy_delete_buffer(buf); return total_meter; }
3. 适配效果验证
你提到的三类输入都可以正常解析:
about 2 1/4 miles:忽略about,计算(2 + 0.25) * 1609.34 = 3621.015米1 mile 40 yards:分段计算1*1609.34 + 40*0.9144 = 1645.916米5m 10y:分段计算5*1 + 10*0.9144 = 14.144米
如果需要新增支持的单位,只需要在Flex中加单位匹配规则、Bison中加对应的换算规则即可,无需修改整体逻辑。
内容的提问来源于stack exchange,提问作者alakit
相关产品推荐
相关产品推荐

