如何在Perl中设置长度并证明Array字段的合理性?

时间:2017-01-12 22:14:41

标签: arrays perl customization

我正在分解文本文件,并将其设置为新文件。代码有效,但我知道格式没有正确排列,因为我是Perl的新手 - 谷歌搜索似乎没有用。在构建阵列后,您可以设置阵列的各个字段长度吗?

while (my $line = <INFILE1>) 
{   
    chomp $line;
    my @tokens = split /\t/, $line;
    $numOfElements = 0;
    $counter = 0;
    foreach $element (@tokens)
    {
        $counter = $counter + 1;
    }

foreach $element (@tokens)
{

    if ($element eq "" or $element eq " ")
    {

    }
    else
    {
        push @shiftedElements, $element;

        $numOfElements = $numOfElements + 1;

    }



}


my @finalElementLine = ($numOfElements);#used to prevent array size` from not matching up with the elements in the new array
    push @finalElementLine, @shiftedElements;#fills the new array 
    $printToFile = " $finalElementLine[1] |   $finalElementLine[2]   |   $finalElementLine[$numOfElements]   |   $finalElementLine[$numOfElements-4]  |  $finalElementLine[$numOfElements-3] | $finalElementLine[$numOfElements-2]  $finalElementLine[$numOfElements-1]\n";




    my $OUTFILE;        
    open $OUTFILE, '>>', $newFile;
    print { $OUTFILE } $printToFile;
    close $OUTFILE;

1 个答案:

答案 0 :(得分:1)

我不确定我是否完全理解这个问题,如果需要请澄清。

正在打印的字段的宽度可以由printf控制,或者您可以按sprintf形成所需长度的字符串。

为了使整个输出很好地排列,你首先需要找到每列中最长字符串的长度,或者至少是最长的字符串长度。由于您一次打印一行,因此您显示的内容不太可能。

my $maxlen = '...';  # decide on or precompute the maximum field width

my $printToFile = join ' |  ', 
    map { sprintf "%${maxlen}s", $_ } @finalElementLine;

map格式化每个元素的长度为$maxlen的字符串,并根据需要用空格填充每个元素。它返回该列表,然后通过问题中使用的内容将join转换为标量。

如果您想在左侧排列它们,请使用sprintf "%-${maxlen}s", $_。我使用s转换(对于字符串),因为没有给出详细信息。请参阅文档并根据需要进行调整。

为了可靠地估计最大字段宽度,您需要首先拥有所有行。如果没有太多数据,您可以将每个已处理的行存储为数组中的arrayref并在末尾进行打印。通过其他简化

use warnings;
use strict;
use List::Util qw(max);

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

while (my $line = <$fh>) 
{   
    chomp $line;
    my @tokens = split /\t/, $line;   

    # Run the explicit loop if other processing is needed, or:
    my @shiftedElements = grep { $_ ne '' and $_ ne ' ' } @tokens;
    my $numOfElements = @shiftedElements;

    # UNCLEAR -- is the first element below necessary?
    # "used to prevent array size from 
    #  not matching up with the elements in the new array"
    my @finalElementLine = ($numOfElements, @shiftedElements);

    push @rows, \@finalElementLine;
}
close $fh;

my $maxlen = max map { length } map { @$_ } @rows;  # for all fields in all rows

open my $OUTFILE, '>>', $newFile or die "Can't open for appending: $!";
foreach my $rline (@rows) 
{
    my $printToFile = join ' |  ', 
        map { sprintf "%${maxlen}", $_ } @$rline;
    print $OUTFILE $printToFile, "\n";
}
close $OUTFILE;

这将打印出具有相同宽度的所有字段。如果某些比非最佳的其他更长,则在这种情况下为每列分别设置字段宽度,并在打印时使用它。这使得打印相当混乱,所以只在必要时才这样做。由于我没有您的数据,因此尚未经过测试,请详细说明。

一些评论

  • 将数组分配给标量时,标量获取数组元素的数量

  • $counter未使用,因此我删除了它。要恢复:my $counter = @tokens;

  • grep中的条件可以使用正则表达式缩短

  • 每一行(@finalElementLine)都存储在@rows中作为arrayref

  • $maxlen:形成所有行中所有字段的列表,然后取长度,然后取最大值

  • $rline的每个元素@rows@$rline 解除引用map

  • 的列表中>
  • 如果实际上不需要$NumOfElements,整个循环会大大简化

    push @rows, [ grep { not /^(?:| )$/ } @tokens ];
    
  • 如果你可以排除任何数量的空间(而不仅仅是一个),那么请使用
    grep { not /^\s*$/ } 只有空格(或没有) - 或 -
    grep { /\S/ }非空格(至少一个)

如果不需要$numOfElements,则处理顺序的摘要为

my @rows = map { 
    my @r = grep { /\S/ } split /\t/; 
    @r ? \@r : (); 
} <$fh>;

虽然这正确地取代了while循环,但这种挤压很可能不适合生产。

列表上下文中的<$fh>返回文件中的所有行,map转换为输出列表,分配给@rows。在map选项卡上的每一行都是split,并且从该列表中筛选出空/仅空格元素。如果()最终没有元素,则返回refarray,或返回空列表@r

map返回的空列表会被其他元素压缩成一个列表,从而有效地从输出中消失。执行map工作是grep的诀窍,过滤掉事情。