在Perl中解析不规则的文本文件

时间:2011-05-19 21:10:51

标签: perl parsing text

我是perl编程的新手,想了解用perl解析文本文件。 我有一个文本文件,其中包含不规则格式,我想将其解析为三个。

基本上该文件包含与以下内容类似的文本:

;out;asoljefsaiouerfas'pozsirt'z
mysql_query("SELECT * FROM Table WHERE (value='true') OR (value2='true') OR (value3='true') ");
1234 434 3454

4if[9put[e]9sd=09q]024s-q]3-=04i
select ta.somefield, tc.somefield 
from TableA ta INNER JOIN TableC tc on tc.somefield=ta.somefield 
INNER JOIN TableB tb on tb.somefield=ta.somefield 
ORDER by tb.somefield
234 4536 234

并且列表以这种格式继续。

所以我需要做的是解析三个。即顶部的那个,获得哈希检查。第二个是mysql查询,第三个是解析这三个数字。出于某种原因,我不知道如何做到这一点。我在perl中使用'open'函数来从文本文件中获取数据。然后我尝试使用'split'函数进行换行,但结果是查询不在一行或一个模式中,所以我不能像我想象的那样使用它。

3 个答案:

答案 0 :(得分:6)

假设:

  1. 数据块之间会有一个空行。
  2. 该空白行仅包含换行符。
  3. 在这些块中,哈希检查将是顶部的单个行,并且这三个数字将是底部的单个行。
  4. 记住这一点:

    use strict;
    use warnings;
    use English qw<$RS $OS_ERROR>;
    
    local $RS = "\n\n";
    
    open( my $fh, '<', $path_to_file ) 
        or die "Could not open $path_to_file! - $OS_ERROR"
        ;
    while ( <> ) { 
        chomp;
        my ( $hash_check_line
           , @inner_lines 
           )
           = split /\n/
           ;
        my @numbers = split /\D+/, pop @inner_lines;
        my $sql     = join( "\n", @inner_lines );
    
        ...
    }
    

    通过将$RS$/$INPUT_RECORD_SEPARATOR)更改为双换行符,我们会更改记录的读取方式。

    这并不是那么奇怪,但在我使用Perl的那些年里,我不得不将记录分隔符设置为一些非常有趣的字符串,但有时只需要读取您想要读取的块即可。

答案 1 :(得分:3)

哦,天啊。

我看到的算法是:

  • 缓存第一行。
  • 阅读所有行,直至出现空行。
  • 最后一行是数字。
  • 其余的都是查询。

考虑到这一点,我提出以下代码:

open my $fh, '<', $path_to_file
    or die "Can't open $path_to_file: $!";
while (my ($checksum, $query, $numbers) = read_record($fh) ) {
    # do something with record
}
close $fh or warn "$!";

sub read_record {
    my $fh = shift;
    my @lines;
    LINE: while (my $line = <$fh>) {
        chomp $line;
        last LINE if $line eq q{}; # if empty, we're done with the record!
        push @lines, $line;        # store it :)
    }
    return unless @lines;          # if we didn't get anything, eof!
    my $checksum = shift @lines;   # first was checksum.
    my $numbers = pop @lines;      # last thing read was numbers.
    my $query = join ' ', @lines;  # everything else, query.
    return ($checksum, $query, $numbers);
}

当然,修改以适应边界条件。

答案 2 :(得分:2)

以下似乎有效:

while ($file_content =~ /\s*^(.+?)^(.*?)^(\d+\s+\d+\s+\d+)$/smg) {
    my $checksum = $1;
    my $query = $2;
    my $numbers = $3;
    # do stuff
}

以下是正则表达式的解释:

\s*                   # eat up empty lines
^(.+?)                # save the checksum line to group 1
^(.+?)                # save one or multiple query lines to group 2
^(\d+\s+\d+\s+\d+)$   # save number line to group 3

第一组总是只有一行,因为当遇到下一行时它是懒惰的,正则表达式会尝试在第二组开始匹配。此时,如果匹配的其余部分可以完成,则第二组将包含数字之前的所有后续行。