Perl:用多行数据填充未知长度的二维数组

Perl: Populate 2D Array of Unknown Length with Multiline Data

背景

我有一个 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* 

一个文本文件可以有零个个分析块,或者它可以有任意数量个分析块——块的数量和每个的大小都是未知的。我希望做的是在二维数组中收集这些块中的信息。例如,如果一个文本文件恰好有 2 个分析块,则二维数组将如下所示:

$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]
            ]                                       
        ];

如果有人有更好的建议来收集数据,同时如上所示将每个分析块放在一起,请告诉我。可能有比我不知道的二维数组更好的解决方案。

尝试

我是 Perl 的新手,但我通过查看 this SO question 了解如何创建二维数组。问题是我不确定如何用我的特定案例填充二维数组。到目前为止,我有以下代码:

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, ;            # 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...]'

问题

我无法思考如何遍历文件的每个部分以创建所需的二维数组。我可能只是盯着它看得太久了。

如何创建我需要的数组?非常感谢所有解释,无论是文字解释还是带有代码示例的解释。


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

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

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.

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

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, ;    # 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. Note that I use qr|...| in the first case above so that () can be used freely inside, what isn't possible in the second one since they are the delimiters. Standard regex docs are -- tutorial perlretut, quick introduction perlrequick, full syntax perlre, and reference perlreref

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

相反,例如,可以使用数组的散列。然后一个块内容的匿名数组将是一个 'value' 作为 ID 的键。在这种情况下,您不会维护订单。这可以用另一个辅助结构来解决,例如,如果需要的话。

这里有一篇关于 Arrays of Arrays and a cookbook on Complex Data structures 的教程。