Perl:使用多行数据填充未知长度的2D数组

时间:2016-05-27 23:01:39

标签: arrays perl multidimensional-array multiline

背景

我有一个Perl程序,它通过目录并解析文本文件以获取某些信息。其中一条信息是分析块,如下所示:

*ANALYSIS_START* [analysis ID]
  Line(s)     = [multi- or single-line Line(s) data]
  Reason Code = [single-line Reason Code data]
  CR          = [single-line CR data]
  Note        = [multi-line Note data]
                [multi-line Note data]
*ANALYSIS_END* 

文本文件可以包含分析块,或者它可能具有这些分析块的任意数量 - 块数和每个块的大小都是未知的。我希望做的是在2D数组中收集这些块中的信息。例如,如果文本文件恰好有2个分析块,则2D数组看起来像这样:

$VAR1 = [
            [                                       
                Lines       = [multi- or single-line data]
                Reason Code = [single-line data]
                CR          = [single-line data]
                Note        = [multi-line data]
                              [multi-line data]
            ]                                       
            [                                       
                Lines       = [multi- or single-line data]
                Reason Code = [single-line data]
                CR          = [single-line data]
                Note        = [multi-line data]
                              [multi-line data]
            ]                                       
        ];

如果某人有更好的建议来收集数据,同时如上所示将每个分析块保持在一起,请告诉我们。可能有一个比我不知道的2D阵列更好的解决方案。

尝试

我对Perl相当新,但我了解如何通过查看this SO question来创建2D数组。问题是,我不确定如何使用我的特定情况填充2D数组。到目前为止,我有以下代码:

while (my $current_line = <$textfile>) {

    # Code that gets other, single-line information from file

    $pattern = '\*ANALYSIS_START\*';
    if ($current_line =~ $pattern) {       # Find Analysis Block
        push @analysis_IDs, $1;            # Get the analysis ID
        while(<$textfile>) {
            last if /\*ANALYSIS_END\*/;    # Stop at block's end
            push @analysis_info, $_;       # Append each line of data
        }
    }
}

当然,这会导致我的数组看起来像这样,文件的每一行都是独立的,但分析块不是:

$VAR1 = ''
$VAR2 = 'Lines       = [lines data]'
$VAR3 = 'Reason Code = [reason code data]'
$VAR4 = 'CR          = [cr data]'
$VAR5 = 'Note        = [note data]'
$VAR6 = '              [note data...]'
$VAR7 = ''
$VAR8 = 'Lines       = [lines data]'
$VAR9 = 'Reason Code = [reason code data]'
$VAR10= 'CR          = [cr data]'
$VAR11= 'Note        = [note data]'
$VAR12= '              [note data...]'

问题

我无法绕过如何遍历文件的每个部分以创建所需的2D数组。我可能只是盯着它看太久了。

如何创建我需要的数组?所有解释,仅限单词或带有代码示例的解释都非常受欢迎。

我的问题可以改进吗?请在评论中告诉我们!

2 个答案:

答案 0 :(得分:2)

这是一种获取问题所需的方法,特别是通过使用数组数组。

use warnings;
use strict;

my $file = 'data_analysis.txt';
open my $fh, '<', $file or die "Can't open $file -- $!";

# Prepare (and compile) START/END paterns, capturing ID in START
my $start_pattern = qr|\*ANALYSIS_START\*\s*\[([^[]+)\]|;
my $end_pattern   = qr(\*ANALYSIS_END\*);

my @analysis_IDs;
my @analysis_info;

while (my $line = <$fh>) 
{
    chomp($line);

    # Code that gets other, single-line information from file

    if ($line =~ $start_pattern .. $line =~ $end_pattern) 
    {   
        if ($line =~ $start_pattern) {
            push @analysis_IDs, $1;    # Get the analysis ID
            push @analysis_info, [];   # Add arrayref this block's lines
        }   
        elsif (not $line =~ $end_pattern) {
            push @{$analysis_info[-1]}, $line;  # add to last []
        }
    }   
}

print "$_\n" for @analysis_IDs;

use Data::Dumper;
print Dumper(\@analysis_info);

代码使用范围运算符..来确定它在模式中的位置。这个有用的运算符可以跨迭代保持状态,因此它知道条件何时满足并且仍然是真的(或不是)。只要第二个条件保持为假,它就会在第一个条件变为(并保持)为真时评估为真。这使我们无法维护一个单独的变量来跟踪所有这些变量。见Range Operators in perlop。由于开始和结束模式需要不同的处理,因此它们(再次)在内部区分。这不是最有效的方式,但我希望它很清楚。

匹配可以使用$line =~ $pattern代替$line =~ /$pattern/,因为已使用qr准备了已使用的模式。显式$line用于寻求清晰,但可以(隐式)使用$_来提供更紧凑的代码。特别是,范围条件简化为(/$start_pattern/ .. /$end_pattern/),我们现在需要分隔符。可以使用几乎任何分隔符(或一对),它也适用于qr运算符。见Quote-like operators in perlop。请注意,我在上面的第一种情况下使用了qr|...|,因此()可以在内部自由使用,而在第二种情况下不可能使用,因为它们分隔符。标准正则表达式文档是 - 教程perlretut,快速介绍perlrequick,完整语法perlre和参考perlreref

使用这种方法,你可以保留一个带有分析ID的单独数组,另一个带有块的数组。他们通过指数达成一致,但这可能不是最可靠的系统。

相反,例如,可以使用数组的散列。然后,块的内容的匿名数组将是密钥的“值”,即ID。在这种情况下,您不会维护订单。这可以通过另一种辅助结构来解决,例如,如果需要的话。

以下是关于Arrays of Arrays的教程和Complex Data structures上的食谱。

答案 1 :(得分:0)

您需要的只是块的另一个数组。然后,当您点击下一个或最后一个块时,将其推入主阵列。

my @analysis_Ids;
my $current_analysis = [];
while (my $current_line = <$textfile>) {
    push @$current_analysis, $_;
    # if next one, push @analysis_Ids, $current_analysis; and reset $current_analysis;
}
# check for the final one.