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 的教程。
背景
我有一个 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 的教程。