【问题标题】:How to split a text file into two arrays?如何将文本文件拆分为两个数组?
【发布时间】:2017-09-09 00:13:24
【问题描述】:

我正在解析一个如下所示的文本文件:

ABCD
EFGH
IJKL

MNOP
QRST
UVWX

是否可以在 Perl 中以产生两个 4x3 数组的方式解析它?例如,array1[2][2] = Kand array2[0][1] = N。 代码:

#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;

open(FH, '<', 'gwas.txt') or die "Couldn't open file $!";

while(<FH>) {

    #parse file into 2 arrays
}
close(FH);

【问题讨论】:

  • 这是一个谜题吗?什么样的计数系统有K 和索引(2,4)N(1,0)?你可以解释吗?但首先——你能告诉我们你做了什么以及你有什么问题吗?这里的问题应该是关于你的代码
  • @zdim 我的错!刚刚修复了这些并添加了代码。
  • 这样更好,谢谢(不过,我认为您的意思是a[2][2] == K ...?) 至于代码:您通过让数据结构携带references来制作多维结构> 每个插槽中的另一个。阅读参考资料:教程perlreftut,以及一本食谱perldsc。你想要数组的数组,在pelrdsc
  • @zdim Jesus 哈哈哈,是的,我就是这个意思
  • 因此,当您阅读文件时:在数组的第一个槽(第 1 行)中放置一个 arrayref - 对包含该行元素的数组的引用。该数组的第二个元素是对包含下一行字母的数组的引用,等等。所以,逐行进行。将每个分解成字母(my @line = split '', $_),将对该数组的引用放在该行的“主”数组的元素中。 (例如,$a[$cnt] = \@line 其中$cnt 从零开始,等等。)等等。试一试。

标签: arrays perl parsing


【解决方案1】:

在评论中解释的过程,精简

my @matrix = map { [ split '', $_ ]  } <$fh>;

菱形运算符 list context 中的&lt;&gt; 返回所有行(参见I/O operators),其中每行都由map 中的块处理,返回的列表分配给@ 987654332@。

split 块中,将每一行 ($_) 拆分为字符 (''),并由该列表 ([...]) 组成 anonymous array。鉴于split 的默认值,这可以写为map { [ split '' ] }

总是使用词法文件句柄,这样更好

my $file = 'gwas.txt';
open my $fh, '<', $file or die "Couldn't open $file: $!";

正如 cmets 中所指出的,这会将整个文件处理为一个数组。要处理两个文本块,每个文本块都有自己的数组,我们可以把它写成一个循环(并使用空行来区分块)。

my @matrix;
my $index = 0;    
while (<$fh>) {
    $matrix[$index++] = [ split '', $_ ];
}

这将创建一个 匿名数组 [ ... ] 与行元素,并将其分配给数组 @matrix 中的 $index 点(并增加索引)。另一种方法是

my @row = split '', $_;
$matrix[$index++] = \@row;

每次迭代都会构造一个新数组并为其分配一个引用。

然后我们需要使用空行来区分块。我们还需要管理这两个数组,这可以通过在另一个数据结构中引用数组(矩阵)来很好地完成,比如数组。

use warnings;
use strict;
use Data::Dump qw(dd);

my $matrices;  # will be an arrayref, for references to matrices

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

my @matrix;
my $index = 0;   
while (<$fh>) {
    chomp;

    if (/^\s*$/) {                     # blank line, done with one matrix
        $index = 0;                    # reset index
        push @$matrices, [ @matrix ];  # store anonymous array for @matrix
    }
    else {
        @matrix[$index] = [ split '', $_ ];
        ++$index;
    }
}
push @$matrices, [ @matrix ];          # the last one in the file

close $fh;

print "Spot check: \$matrices->[0][2][2]: $matrices->[0][2][2]\n";
dd($matrices);

这包含对数据的假设,通常它具有确切的预期格式。

请参阅参考教程perlreftut 和数据结构食谱perldsc

另请参阅answer by xxfelixxx,这一切的方式略有不同。

还有很多其他方法可以做到这一点。

【讨论】:

  • 好吧,太棒了,这是有道理的。但我的意图是有两个独立的数组,目前只有一个。
  • @KyleWeise 哦,我现在明白你所说的两个数组是什么意思了。你是对的 - 这会将两个文本块存储为一个,空行上一团糟。这是一个基本的演示。对于您的整个问题,如该评论中所述,将其写成一个循环。当您遇到空行时,从第二个数组开始。
  • 我有点困惑,“上面的内容应该写成循环遍历每个数组元素的行,空行可以用来开始下一个块。”可以你也许用不同的词来形容?
  • @KyleWeise 在答案中添加了完整的程序,谢谢您的评论。
  • 所以我正在玩这个并且发生了一些奇怪的事情。 print 语句适用于 matrices-&gt;[0][2][2] ,但当我将 0 切换为 1 时无效。所以 0 给了我 K,这是正确的,但是如果我替换它们,输出不应该是 W 吗?
【解决方案2】:
#!/usr/bin/env perl
use strict;
use warnings;

my $arrays = [];
my $count = 0;
my $row = 0;

# Read data and store in $arrays
while(<DATA>) {
    if (my ($letters) = m/^(\w+)\s*$/) {
        # Store letters
        $arrays->[$count]->[$row] = [ split //, $letters ];
        $row++;
    } else {
        # Next batch
        $count++;
        $row = 0;
    }
}

# Print it out with indices
for my $iarray ( 0 .. $count ) {
    print "------ Matrix $iarray ------\n";
    my @rows = @{ $arrays->[$iarray] };
    for my $irow ( 0 .. $#rows) {
        my @cols = @{ $rows[$irow] };
        for my $icol ( 0 .. $#cols ) {
            print "($irow,$icol) -> " . $cols[$icol] . "\n";
        }
    }
}

__DATA__
ABCD
EFGH
IJKL

MNOP
QRST
UVWX

输出

------ Matrix 0 ------
(0,0) -> A
(0,1) -> B
(0,2) -> C
(0,3) -> D
(1,0) -> E
(1,1) -> F
(1,2) -> G
(1,3) -> H
(2,0) -> I
(2,1) -> J
(2,2) -> K
(2,3) -> L
------ Matrix 1 ------
(0,0) -> M
(0,1) -> N
(0,2) -> O
(0,3) -> P
(1,0) -> Q
(1,1) -> R
(1,2) -> S
(1,3) -> T
(2,0) -> U
(2,1) -> V
(2,2) -> W
(2,3) -> X

【讨论】:

    【解决方案3】:
    1. 将数据拆分为段落数组。
    2. 将每个段落拆分为行数组。
    3. 将每一行拆分成一个字符数组。

    这可以通过以下任一方式实现:

    my @arrays;
    {
       local $/ = "";  # Paragraph mode
       @arrays = map { [ map { [ split // ] } split /\n/ ] } <>;
    }
    

    my @arrays;
    {
       local $/ = "";  # Paragraph mode
       push @arrays, [ map { [ split // ] } split /\n/ ] while <>;
    }
    

    他们生产:

    $VAR1 = [
              [
                [ 'A', 'B', 'C', 'D' ],
                [ 'E', 'F', 'G', 'H' ],
                [ 'I', 'J', 'K', 'L' ]
              ],
              [
                [ 'M', 'N', 'O', 'P' ],
                [ 'Q', 'R', 'S', 'T' ],
                [ 'U', 'V', 'W', 'X' ]
              ]
            ];
    

    所以,

    say $arrays[0][2][2];  # K
    say $arrays[1][0][1];  # N
    

    【讨论】:

    • 谢谢!这似乎很简单。你能用开/闭花括号解释原因吗?是和@arrays的声明有关还是有别的意思?
    • 它们是mapmap BLOCK LIST 语法的一部分。
    • 您能否介绍一下您是如何打印输出的?我假设 Data::Dumper 因为格式。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多