我想解析一种基于文本的文件格式,它具有略微古怪的语法。以下是一些有效的示例行:
<region>sample=piano C3.wav key=48 ampeg_release=0.7 // a comment here
<region>key = 49 sample = piano Db3.wav
<region>
group=1
key = 48
sample = piano D3.ogg
对于我来说,想出一个能够理解它的正则表达式对我来说太复杂了,但我想知道是否有一种很好的方法可以在不编写自己的解析器的情况下对这种输入进行标记?即我想要读取上述输入并吐出“令牌”流的内容,例如,我的示例格式开头的输出将是:
new Region(), new Sample("piano C3.wav"), new Key("48"), new AmpegRelease("0.7"), new Region()
是否有一个好的图书馆/教程可以指出我正确的方向来实现这个优雅的方式?
更新:我用Irony尝试了这个,但是我需要解析的语法怪癖(特别是sample =下面的数据可能有空格)让他们建议我可能更好地编写基于String.Split的自己的代码。请参阅讨论here。
答案 0 :(得分:2)
对于这种类型的东西,我会得到轻量级但强大的CoCo/R。如果你给我看一些样本输入,我可能会想出一个语法起点。
之前我使用过lex和yacc,所以我有一些解析经验。 - Mark Heath 17分钟前
嗯,你很幸运:我在Fedora的soundfont-utils软件包中找到了sfz
的lex语法。该软件包包含sfz2pat util。你可以在这里获得(源)包:
http://rpmfind.net//linux/RPM/fedora/14/i386/soundfont-utils-0.4-10.fc12.i686.html (src.rpm)
根据快速调查,该语法的最新版本是从2004年11月开始的,但相当复杂(sfz2pat.l为58k)。这是一个尝试的样本:
%option noyywrap
%option nounput
%option outfile = "sfz2pat.c"
nm ([^\n]+".wav"|[^ \t\n\r]+|\"[^\"\n]+\")
ipn [A-Ga-g][#b]?([0-9]|"-1")
%s K
%%
"//".* ;
<K>"<group>" {
int i;
leave_region();
leave_group();
if (!enter_group()) {
SFZERR
"Can't start group\n");
return 1;
}
am_in_group_scope = TRUE;
for (i = FIRST_SFZ_PARM; i < MAX_SFZ_PARM; i++) group_parm[i] = default_parm[i];
for (i = 0; i < MAX_FLOAT_PARM; i++) group_flt_parm[i] = default_flt_parm[i];
group_parm[REGION_IN_GROUP] = current_group;
BEGIN(0);
}
<K>"<region>" {
int i;
if (!am_in_group) {
SFZERR
"Can't start region outside group.\n");
return 1;
}
leave_region();
if (!enter_region()) {
SFZERR
"Can't start region\n");
return 1;
}
am_in_group_scope = FALSE;
for (i = 0; i < MAX_SFZ_PARM; i++) region_parm[i] = group_parm[i];
for (i = 0; i < MAX_FLOAT_PARM; i++) region_flt_parm[i] = group_flt_parm[i];
BEGIN(0);
}
<K>"sample="{nm} {
int i = 7, j;
unsigned namelen;
if (yytext[i] == '"') {
i++;
for (j = i; j < yyleng && yytext[j] != '"'; j++) ;
}
else j = yyleng;
namelen = (unsigned)(j - i + 1);
sfzname = strncpy( (char *)malloc(namelen), yytext+i, (unsigned)(j-i) );
sfzname[j-i] = '\0';
for (i = 0; i < (int)namelen; i++) if (sfzname[i] == '\\') sfzname[i] = '/';
SFZDBG
"Sample name is \"%s\"", sfzname);
SFZNL
if (read_sample(sfzname)) {
#ifndef LOADER
fprintf(stderr, "\n");
#endif
return 0;
}
BEGIN(0);
}
[...snip...]
答案 1 :(得分:1)
假设语言相当规律,我建议使用ANTLR编写一个快速解析器。对于有解析经验的人来说,它有一个非常简单的学习曲线,并输出C#(以及其他内容)。
答案 2 :(得分:1)
我使用Gardens Point LEX和Gardens Point Parser Generator来生成解析器。它们运作良好,特别是如果你有一些lex / yacc知识。
IMO,这两个是.NET的最佳解析器生成器。
一个奖励点:创作者可以快速响应错误报告和建议here。