有没有一种简单的方法可以将文本文件分块到支撑平衡部分?

时间:2009-06-15 23:14:15

标签: perl algorithm parsing recursive-descent

我正在尝试使用Perl& amp解析文件中的一些数据。解析:: RecDescent的。我无法在perl脚本中抛出完整的数据文件,因为RecDescent需要几天的时间。因此,我将庞大的数据文件拆分为RD大小的块,以减少运行时间。

但是,我需要在平衡括号内提取部分,而我现在的例程并不健全(它过分依赖于来自换行符的最终闭括号的位置)。例如:

cell ( identifier ) {
  keyword2 { };
  ...
  keyword3 { keyword4 {  } };
}

...more sections...

我需要抓取从cell ... {到匹配的结尾}的所有内容,这些结尾{{1}}可以有不同数量的间距和子部分。

必须有一些linux命令行才能轻松完成这项工作吗?有什么想法吗?

编辑:输入文件大约为8M,语法大约为60条。

3 个答案:

答案 0 :(得分:5)

显示您正在喂食Parse :: RecDescent;有可能做得更好。

或者您可以尝试使用Text::Balanced来解析{...}。

答案 1 :(得分:3)

为什么RecDescent需要这么长时间?是因为你的语法很复杂吗?如果是这种情况,你可以使用Parse :: RecDescent两个双层传递。我们的想法是,您将定义一个简单的语法来解析单元格... {...},然后将解析后的输出从第一个解析器传递给Parse :: RecDescent,并使用更复杂的语法。这是猜测RecDescent数据速度慢的原因。

另一种选择是编写自己的简单解析器,匹配单元格条目,计算到目前为止看到的大括号数,然后在右大括号计数等于左大括号计数时找到匹配的大括号。这应该很快,但上面的建议实施起来可能更快,更容易维护。

编辑:你一定要用简化的语法尝试Parse :: RecDescent。递归下降解析的算法复杂度与可能的解析树的数量成正比,这可能类似于B ^ N,其中B是语法中的分支点数,N是节点数。

如果您想尝试使用自己的简单解析器进行输入的第一次传递,以下代码可以帮助您入门。

#!/usr/bin/perl -w

use strict;

my $input_file = "input";
open FILE, "<$input_file" or die $!;

my $in_block = 0;
my $current_block = '';
my $open_bracket_count = 0;
while( my $line = <FILE> ) {
    if ( $line =~ /cell/ ) {
        $in_block = 1;
    }

    if ( $in_block ) {
        while ( $line =~ /([\{\}]{1})/g ) {
            my $token = $1;
            if ( $token eq '{' ) {
                $open_bracket_count++;
            } elsif ( $token eq '}' ) {
                $open_bracket_count--;
            }
        }

        $current_block .= $line;
    }

    if ( $open_bracket_count == 0 && $current_block ne '' ) {
        print '-' x 80, "\n";
        print $current_block, "\n";
        $in_block = 0;
        $current_block = '';
    }
}
close FILE or die $!;

编辑:更改代码以避免将整个文件拖入内存。虽然这对于8MB文件来说是微不足道的,但是逐行读取文件会更简洁。

答案 2 :(得分:1)

使用yapp LALR(1)解析器,它在线性时间和常量空间中工作。