我刚开始使用Flex,似乎无法弄清楚如何匹配以下表达式:
"Dog".*"Cat"
------------------
Input :
Dog Ca Cat Cc Cat
------------------
Output:
Dog Ca Cat Cc Cat
但我想要一个非贪婪的匹配,输出如下:
Output:
Dog Ca Cat
如何在Flex上实现这一目标?
修改 的
尝试以下方法:
%%
Dog.*Cat/.*Cat printf("Matched : ||%s||", yytext);
dog.*cat printf("Matched : ||%s||", yytext);
dOg[^c]*cAt printf("Matched : ||%s||", yytext);
DOG.*?CAT printf("Matched : ||%s||", yytext);
%%
输入:
Dog Ca Cat Cc Cat
dog Ca cat Cc cat
dOg Ca cAt Cc cAt
DOG CA CAT CC CAT
输出
Matched : ||Dog Ca Cat Cc Cat||
Matched : ||dog Ca cat Cc cat||
Matched : ||dOg Ca cAt|| Cc cAt
Matched : ||DOG CA CAT CC CAT||
还收到警告:
lex4.l:2: warning, dangerous trailing context
Flex版本:
flex 2.5.35 Apple(flex-31)
答案 0 :(得分:5)
这是使用lex / flex工具的常见问题,这些工具会让初学者(有时候是非初学者)难倒。该问题有两种解决方案,需要两种不同的工具高级功能。像dog ... cat
这样的短语与各种编程语言中的匹配评论大致相同,例如 C 评论表单/* ... */
甚至'comment' ... 'tnemmoc'
。它们与您的示例具有完全相同的特征。请考虑以下 C 代码:
/* This is a comment */ "This is a String */"
这种贪婪的词汇匹配会与错误的评论终结符相匹配(对学生词法分子BTW来说是一个很好的考验!)。
有几个大学编译课程的建议解决方案。解释得很好的是here (at Manchester)。其中引用了几本也涵盖问题的好书:
所描述的两种技术是使用Start Conditions明确指定状态机,或manual input直接读取字符。
对于您的cat ... dog
问题,可以通过以下方式对其进行编程:
在这个解决方案中,我们需要几种状态。关键字dog
会导致其进入DOG
状态,直到遇到字母c
为止。然后进入LETTERC
状态,必须后跟字母a
,否则DOG
状态继续;字母a
会导致输入CAT
状态,后面必须跟一个字母t
,这会导致整个短语匹配并返回INITIAL
状态。 yymore
导致整个dog ... cat
文本被保留以供使用。
%x DOG LETTERC CAT
d [dD]
o [oO]
g [gG]
c [cC]
a [aA]
t [tT]
ws [ \t\r\n]+
%%
<INITIAL>{d}{o}{g} {
BEGIN(DOG);
printf("DOG\n");
yymore();
}
<DOG>[^cC]*{c} {
printf("C: %s\n",yytext);
yymore();
BEGIN(LETTERC);
}
<LETTERC>{a} {
printf("A: %s\n",yytext);
yymore();
BEGIN(CAT);
}
<LETTERC>[^aA] {
BEGIN(DOG);
yymore();
}
<CAT>{t} {
printf("CAT: %s\n",yytext);
BEGIN(INITIAL);
}
<CAT>[^tT] {
BEGIN(DOG);
yymore();
}
<INITIAL>{ws} /* skip */ ;
手动输入法只匹配起始短语dog
和输入 C 代码,该代码吞下输入字符,直到遇到所需的cat
序列。 (我没有打扰大写和小写字母)。此解决方案的问题在于难以将输入文本值保留在yytext中以供稍后在解析器中使用。它会丢弃它,如果构造是注释,那就没关系,但是没有其他用处。
d [dD]
o [oO]
g [gG]
ws [ \t\r\n]+
%%
{d}{o}{g} {
register int c;
for ( ; ; )
{
/* Not dealt with upper case .. left as an exercise */
while ( (c = input()) != 'c' &&
c != EOF )
; /* eat up text of dog */
if ( c == 'c' )
{
if ( ( c = input()) == 'a' )
if ( (c = input()) == 't' )
break; /* found the end */
}
if ( c == EOF )
{
REJECT;
break;
}
}
/* because we have used input() yytext always contains "dog" */
printf("cat: %s\n", yytext);
}
{ws} /* skip */ ;
(这两种解决方案都经过测试)
答案 1 :(得分:0)
一个好问题。这是一个纯正则表达式解决方案,不使用非贪婪的.*?
语法:
Dog([^C]|C+(aC+)*([^Ca]|a[^Ct]))*C+(aC+)*at
答案 2 :(得分:0)
这是针对此问题的最小 C++ flex 词法分析器。非贪婪匹配的关键是 flex 手册和其他地方提到的开始条件。
开始条件只是词法分析器的另一种状态。当需要非贪婪匹配时,有些模式需要在第一次匹配时终止匹配
通常,无论状态如何,如果您正在寻找目标字符串或模式,您只需要确保没有其他更通用的模式可以匹配包含目标模式的更长输入段
当目标模式是有条件的并且需要在一些较早的匹配后启用时,启动条件会有所帮助。您打开开始条件以启用匹配目标模式,然后通过将状态重置为 0 或 INITIAL
将其关闭 - 或者切换到另一个状态以进行更多条件匹配
状态通过 BEGIN
切换 - 还有一个状态堆栈可通过 yy_push_state
和 yy_pop_state
使用
flex 手册中有很多启动条件的例子
以下是显示非贪婪匹配与 flex 开始条件的 flex 规则 - 词法分析器匹配一行上第一次出现的狗,直到第一次出现 cat - 匹配不区分大小写
完整的文件放在最后——对于不熟悉 flex 的人,请注意许多行以空格开头——这不是偶然的,也是 flex 所要求的
%%
/* flex rules section */
string match;
dog {
// found a dog, change state to HAVE_DOG to start looking for a cat
BEGIN(HAVE_DOG);
// save the found dog
match = yytext;
}
/* save and keep going till cat is found */
<HAVE_DOG>. match += yytext;
<HAVE_DOG>cat {
// save the found cat
match += yytext;
// output the matched dog and cat
cout << match << "\n";
// ignore rest of line
BEGIN(SKIP_LINE);
}
/* no cat on this line, reset state */
<HAVE_DOG>\n BEGIN(0);
/* rules to ignore rest of the line then reset state */
<SKIP_LINE>{
.*
\n BEGIN(0);
}
/* nothing to do yet */
.|\n
这是一些测试输入
$ cat dogcat.in.txt
Dog Ca Cat Cc Cat
dog Ca cat Cc cat
dOg Ca cAt Cc cAt
DOG CA CAT CC CAT
cat dog dog cat cat
dog kitten cat dog cat
dig cat dog can dog cut
dig dug dog cow cat cat
doc dogcat catdog
dog dog dog
cat cat cat
构建
flex -o dogcat.flex.cpp dogcat.flex.l && g++ -o dogcat dogcat.flex.cpp
一起跑
$ ./dogcat < dogcat.in.txt
Dog Ca Cat
dog Ca cat
dOg Ca cAt
DOG CA CAT
dog dog cat
dog kitten cat
dog cow cat
dogcat
完整的 flex 文件
/* dogcat.flex.l */
/*
Build with:
flex -o dogcat.flex.cpp dogcat.flex.l && g++ -o dogcat dogcat.flex.cpp
*/
/*
A minimal C++ flex lexer that shows nongreedy matching with flex
start conditions
matches the first occurrence of dog on a line till the first
occurrence of cat
matching is case insensitive
*/
/* C++ lexer using yyFlexLexer in FlexLexer.h */
%option c++
/* case-insensitive patterns */
%option case-insensitive
/* generate main function for executable */
%option main
/* all input must be matched, no echo by default */
%option nodefault
/* debug output with lexer.set_debug(1) */
%option debug
/* start condition means dog was matched */
%x HAVE_DOG
/* start condition means to ignore remaining line */
%x SKIP_LINE
%{
#include <string>
#include <iostream>
// C++ flex lexer class
// needed because header itself has no guard
#ifndef yyFlexLexerOnce
# include <FlexLexer.h>
#endif
using namespace std;
namespace {
// the C++ lexer class from flex
yyFlexLexer lexer;
// main generated by flex still calls free yylex function even for C++ lexer
int yylex() {
return lexer.yylex();
}
}
%}
%%
/* flex rules section */
string match;
dog {
// found a dog, change state to HAVE_DOG to start looking for a cat
BEGIN(HAVE_DOG);
// save the found dog
match = yytext;
}
/* save and keep going till cat is found */
<HAVE_DOG>. match += yytext;
<HAVE_DOG>cat {
// save the found cat
match += yytext;
// output the matched dog and cat
cout << match << "\n";
// ignore rest of line
BEGIN(SKIP_LINE);
}
/* no cat on this line, reset state */
<HAVE_DOG>\n BEGIN(0);
/* rules to ignore rest of the line then reset state */
<SKIP_LINE>{
.*
\n BEGIN(0);
}
/* nothing to do yet */
.|\n