2016-03-15 59 views
0

作为Flex-Bison的首发者,我遇到了第一个障碍,似乎无法找到通路。使用Flex-Bison在标签之间提取数据

问题说明:对于给定的html/xml文件,需要在标签之间提取数据。我已经阅读了SO的相关问题,但似乎没有达到这个问题的最佳位置,虽然

)因为这是为了学习如何使用flex-bison,我不想切换到使用任何其他语言/工具)。

输入文件包含以下字段提取:

<!DOCTYPE html> 
<html charset="utf-8" lang="en"> 
<head> 
<meta content="text/html; charset=UTF-8" http-equiv="content-type"> 
<meta content="text/css" http-equiv="Content-Style-Type"> 
<script src="/commd/jquery.nivo.slider.pack.js"></script> 
<link rel="stylesheet" type="text/css" href="/fonts/stylesheet.css"/> 
<link rel="stylesheet" type="text/css" href="/commd/stylesheet.css"/> 


<!--<legend> DATA TO BE EXTRACTED</legend>--> //relevant data between <legend> tag 

我写了下面的扫描仪test.l

%option noyywrap 
%{ 
#include "parser.tab.h" 
%} 
%% 
"<!--<legend>" {return name1;} 
(.*?) {yylval.sval=strdup(yytext); return name2;} 
"<\/legend>" {return name3;} 
%% 

和解析器代码parser.y

%{ 
#include<stdio.h> 
#include<string.h> 
#include<stdlib.h> 
#define YYERROR_VERBOSE 
extern int yylex(); 
extern int yyparse(); 
extern FILE *yyin; 

%} 

%union { 
    char *sval; 
} 

%token <sval> name1 
%token <sval> name2 
%token <sval> name3 

%% 
names : name1 name2 name3 { printf("%s\n", $2); } 

%% 

int main(int argc, char **argv) { 

    // open a file handle to a particular file: 
    FILE *myfile = fopen(argv[1], "r"); 
    // make sure it is valid: 
    if (!myfile) { 
     printf("I can't open file!"); 
     return 1; 
    } 
    // set flex to read from it instead of defaulting to STDIN: 
    yyin = myfile; 

    // parse through the input until there is no more: 
    do { 
     yyparse(); 
    } while (!feof(yyin)); 

} 

void yyerror(char *s) { 
    printf("EEK, parse error! Message:%s",s); 
    // might as well halt now: 
    exit(1); 
} 

使用makefile编译

all: compile_run 

compile_run: 
    @bison -d parser.y 
    @flex test.l 
    @gcc parser.tab.c lex.yy.c -lfl -o run 

上执行的程序,但是我得到以下错误:

EEK, parse error! Message:**syntax error, unexpected name2, expecting name1 ***

我明白读书的错误,作为name2令牌可以无限,也它显示之前预期从grammmar令牌name1匹配。

我的问题是,现在我已经定义了语法首先查找name1,然后name2然后name3 token为什么应该有这个错误。

如果我在扫描仪只定义一个令牌name1

<!--<legend>(.*?)<\/legend> {return name1;} 

我会得到包括标签的整个字符串。我可以发布过程来获取数据,但我真的觉得必须有一个更聪明的方法,我会从这里知道:)。

回答

3

您遇到问题的原因是您只为输入文件的一部分定义了规则,并希望词法分析器和分析器将其余部分忽略。这不是工具的工作方式;他们尝试并匹配所有内容,因此您必须为输入数据的每个方面定义所有内容。

我还注意到您的原始词法分析器文件没有用flex构建。你有错误的顺序规则。你原来的规则集:

%option noyywrap 
%{ 
#include "parser.tab.h" 
%} 
%% 
"<!--<legend>" {return name1;} 
(.*?) {yylval.sval=strdup(yytext); return name2;} 
"<\/legend>" {return name3;} 
%% 

提供以下错误:

"test.l", line 8: warning, rule cannot be matched

这是因为Flex使用规则,以便和name3永远不能返回的模式为name2也将匹配name3 。你明显地解决了这个问题,以便能够构建你的测试程序。解决方法是反向的规则的顺序,如:弯曲(野牛)的

%option noyywrap 
%{ 
#include "parser.tab.h" 
%} 
%% 
"<!--<legend>" {return name1;} 
"<\/legend>" {return name3;} 
(.*?) {yylval.sval=strdup(yytext); return name2;} 
%% 

一个特点,那就是在调试有用的是调试模式,这并不奇怪!

如果我们运行与调试模式下的代码启用,像这样:

bison -d parser.y 
flex -d test.l 
gcc parser.tab.c lex.yy.c -lfl -o run 

,然后执行该程序,我们现在得到的词法分析器有帮助输出:

--(end of buffer or a NUL)
--accepting rule at line 8 ("")
EEK, parse error! Message:syntax error, unexpected name2, expecting name1

你可以看到您的规则(.*?)确实符合任何文字,但不仅限于<legend>,而且也包含在其他地方。这意味着您的解析器在看到name1之前将会看到一系列令牌name2,name2,name2。现在您的只有规则在解析器中说在输入必须令牌开始name1,因此你得到一个语法错误。

现在,有几种方法可以解决这个问题。您可以将您的野牛规则更改为在name1之前接受许多name2令牌,或者可以升级整个语法来描述整个XML/HTML。至少你可能想升级语法来接受几个<legend>标签在一个文件中!目前,你的语法只匹配一个包含一个单一<legend>结构的文件,而不是别的 - 记住它不会忽略其他输入(除非你告诉它)!

重写一般化XML结构的语法会更大,但可以做的是指示flex词法分析器忽略其他输入,以便不返回name2模式。我们只需为输入数据文件中的其他内容编写模式。我们需要匹配其他XML标签,注释行和空白区域,并告诉flex忽略它们。

这样做可能是一个例子:

%{ 
#include "parser.tab.h" 
%} 
%% 
"<!--<legend>"   {return name1;} 
"<\/legend>"    {return name3;} 
"<".[^-](.|[ \t])*">" ; /* Skip other tags */ 
"//".*[\r\n]+   ; /* Skip comments */ 
[\r\n\t ]+    ; /* Skip unused whitespace */ 
(.*?)     {yylval.sval=strdup(yytext); return name2;} 
%% 

当我们运行这段代码我们设法跳过一些不必要的标签:

--(end of buffer or a NUL) 
--accepting rule at line 7 ("<!DOCTYPE html>") 
--accepting rule at line 9 (" 
") 
--accepting rule at line 7 ("<html charset="utf-8" lang="en">") 
--accepting rule at line 9 (" 
") 
--accepting rule at line 7 ("<head>") 
--accepting rule at line 9 (" 
") 
--accepting rule at line 7 ("<meta content="text/html; charset=UTF-8" http-equiv 
="content-type">") 
--accepting rule at line 9 (" 
") 
--accepting rule at line 7 ("<meta content="text/css" http-equiv="Content-Style- 
Type">") 
--accepting rule at line 9 (" 
") 
--accepting rule at line 7 ("<script src="/commd/jquery.nivo.slider.pack.js"></s 
cript>") 
--accepting rule at line 9 (" 
") 
--accepting rule at line 7 ("<link rel="stylesheet" type="text/css" href="/fonts 
/stylesheet.css"/>") 
--accepting rule at line 9 (" 
") 
--accepting rule at line 7 ("<link rel="stylesheet" type="text/css" href="/commd 
/stylesheet.css"/>") 
--accepting rule at line 9 (" 


") 
--accepting rule at line 10 ("<!--<legend> DATA TO BE EXTRACTED</legend>--> //re 
levant data between <legend> tag") 
EEK, parse error! Message:syntax error, unexpected name2, expecting name1 

我们打了另一个问题。神秘的是,它为什么不匹配并返回name1?这是由匹配算法贪婪引起的,并找到匹配最长令牌的规则。为了克服这个问题,我们必须使用flex的start condition特性,并且只能在<legend>结构内匹配一般文本。在匹配XML中使用起始条件时,我们必须小心,因为<符号用于表示状态更改以及引入XML标记。我们可以重新编码切换状态是这样的:

%{ 
#include "parser.tab.h" 
%} 
%x legends 
%x finishd 
%% 
<INITIAL>"<!--<legend>"   {BEGIN(legends); return name1;} 
<finishd>"</legend>-->"   {BEGIN(INITIAL); return name3;} 
<INITIAL>"<".[^-](.|[ \t])*">" ; /* Skip other tags */ 
<INITIAL>"//".*[\r\n]+   ; /* Skip comments */ 
<INITIAL>[\r\n\t ]+    ; /* Skip unused whitespace */ 
<legends>[^<>]+     {BEGIN(finishd); yylval.sval=strdup(yytext); return name2;} 
%% 

那么神奇,我们得到如下:

--(end of buffer or a NUL) 
--accepting rule at line 9 ("<!DOCTYPE html>") 
--accepting rule at line 11 (" 
") 
--accepting rule at line 9 ("<html charset="utf-8" lang="en">") 
--accepting rule at line 11 (" 
") 
--accepting rule at line 9 ("<head>") 
--accepting rule at line 11 (" 
") 
--accepting rule at line 9 ("<meta content="text/html; charset=UTF-8" http-equiv 
="content-type">") 
--accepting rule at line 11 (" 
") 
--accepting rule at line 9 ("<meta content="text/css" http-equiv="Content-Style- 
Type">") 
--accepting rule at line 11 (" 
") 
--accepting rule at line 9 ("<script src="/commd/jquery.nivo.slider.pack.js"></s 
cript>") 
--accepting rule at line 11 (" 
") 
--accepting rule at line 9 ("<link rel="stylesheet" type="text/css" href="/fonts 
/stylesheet.css"/>") 
--accepting rule at line 11 (" 
") 
--accepting rule at line 9 ("<link rel="stylesheet" type="text/css" href="/commd 
/stylesheet.css"/>") 
--accepting rule at line 11 (" 


") 
--accepting rule at line 7 ("<!--<legend>") 
--accepting rule at line 12 (" DATA TO BE EXTRACTED") 
--accepting rule at line 8 ("</legend>-->") 
DATA TO BE EXTRACTED 
--accepting rule at line 11 (" ") 
--(end of buffer or a NUL) 
--accepting rule at line 10 ("//relevant data between <legend> tag 
") 
--(end of buffer or a NUL) 
--EOF (start condition 0 

现在,如果你关闭了柔性调试,你只会得到所需的输出:

DATA TO BE EXTRACTED

如果有超过一组数据需要提取,您仍然需要更新野牛语法;实际上你应该升级整个bison语法来更好地匹配更多的XML。至少我已经解释过,教程的时尚,发生了什么以及使它与你的样本数据集一起工作的一种方法。

+0

非常感谢!如何调整flex的调试? –

+0

发现它(:必须编译flex而不使用-d选项。 –

相关问题