Perl Разбор без пакета - PullRequest
       0

Perl Разбор без пакета

1 голос
/ 05 февраля 2020

У меня есть CSV-файл, содержащий данные, которые я хотел бы проанализировать и сохранить в некоторой структуре данных для печати на экране. У меня нет возможности установить какие-либо пакеты или модули, которые не были предварительно установлены. Я знаком с текстовым модом CSV, но не могу его использовать, поэтому мне нужно сделать это вручную.

Данные выглядят так:

Name,Age,Weight,Target  
April,     23,    134,    90  
Jenna,     45,    156,    90  
Matt,      12,    90,     90  
Aaron,     34,    190,    90  
Daniel,    22,    188,    90  

Вот то, что у меня пока есть, но это просто сохраняет все данные в массив и распечатывает их.

use strict;
use warnings;
use Data::Dumper;

my $file = "file.csv";

my %people;
my @data;

open my $fh, $file or die "Could not open $file: $!";
while (my $line = <$fh>) {
    chomp $line;
    my @fields = split(/,/, $line);
    push @data, @fields;
}
close $fh;

print join(", ", @data);

Это дает следующий вывод:

Name, Age, Weight, Target, April        ,          23,       134,     90, 

Интервал обусловлен разнесением столбцов csv. Строка заголовка не имеет пробелов. Я хотел бы более организованный способ хранения значений каждого столбца, а затем распечатывать их на экране.

Ответы [ 3 ]

4 голосов
/ 05 февраля 2020

моя работа очень строга относительно использования чего-либо, что не было предварительно установлено.

Ах, хорошо. Об этом можно много говорить, некоторые из них упоминаются в комментариях. Но я бы оставил это на этом этапе, так как вопрос достаточно ясен и четко сформулирован.

Если ваши данные всегда отображаются, значит все просто. Но я предлагаю также добавить код, который проверяет наличие гремлинов в ваших данных, то есть вещи, которые исключают ручной анализ; предварительная обработка сортов. Так что вы получите предупреждение, когда это произойдет.

Сказав это, и с хорошим использованием форматов в другом ответе, я хотел бы прокомментировать код.

Проблема в том, что строка

push @data, @fields;

оценивает @fields в список своих элементов, а затем добавляет эти элементы в массив - она ​​не каким-то образом "добавляет массив" @fields как единый объект, который я Предположим, что вы ожидали. Так как он продолжает проходить по линиям, он продолжает строить этот длинный массив со всеми данными в одном длинном плоском списке.

Вместо этого добавьте ссылку на массив @fields

while (my $line = <$fh>) {
    chomp $line;
    my @fields = split /\s*,\s*/, $line;
    push @data, \@fields;
}

, где Я также сократил пробелы, как только мы на это. (CSV вообще не должен иметь их.)

Здесь мы можем просто взять ссылку @fields, потому что она объявляется заново для каждой итерации. Если бы оно было объявлено в другом месте и просто перезаписывалось в каждой итерации, вам нужно было бы скопировать его (в анонимный массив) вместо

while (my $line = <$fh>) {
    chomp $line;
    @fields = split /\s*,\s*/, $line;   # if @fields is declared outside
    push @data,  [ @fields ];
}

, иначе вы бы получили одну и ту же ссылку для всех элементов @data.

Теперь элементы @data являются ссылками на строки и могут обрабатываться индивидуально. Например,

use List::Util qw(max);  

my $max_name_wt = max map { length $_->[0] } @data;

printf "%${max_name_wt}s %6s %6s %6s\n", @{ shift @data };  # headers

foreach my $row (@data) {
    printf "%${max_name_wt}s %6d %6d %6d\n", @$row;
}

Предполагается, что все числа целые и содержат не более 6 цифр. Также предполагается, что поля не пропущены, или их undef будет выдавать предупреждения в printf. List :: Util является основным модулем.

Существуют более простые способы печати сложных структур данных; см ядро ​​ Данные :: Дампер .

1 голос
/ 05 февраля 2020

Если вам нужна холодная печать на экране и поля в ваших файлах одинаковы, попробуйте следующее:

#!/usr/bin/perl

use strict;
use warnings;

open(CSV, "< file.csv") or die "Can't open input file!\n";
my ($name, $age, $weight, $target);
format STDOUT =
@<<<<<<<<<@<<<<<<<<<@<<<<<<<<<@<<<<<<<<<
$name,    $age,     $weight,  $target
.
while ( my $line = <CSV> ) {
    chomp($line);
    ($name, $age, $weight, $target) = split(/,\s*/, $line);
    write;
}
close(CSV);

output:

$ ./parse_csv.pl 
Name      Age       Weight    Target
April     23        134       90
Jenna     45        156       90
Matt      12        90        90
Aaron     34        190       90
Daniel    22        188       90
0 голосов
/ 05 февраля 2020

ОП не обладает полным пониманием сложной структуры данных.

Пожалуйста, смотрите код ниже, который заполняет данные га sh. С данными можно манипулировать любым мыслимым способом.

use strict;
use warnings;
use feature 'say';

use Data::Dumper;

my $debug = 1;                          # debug flag

my %people;                             # store people's data

while(<DATA>){
    next if /^\s*$/;                    # skip empty lines
    next if /Name\,Age/;                # skip header
    s/\s+//g;                           # remove spaces
    my @data = split ',';               # obtain data
    my %param;                          # temp hash 
    @param{qw/age weight target/} = @data[1..3];
    $people{$data[0]} = \%param;        # store param hash reference
}

say Dumper(\%people) if $debug;

$~ = 'STDOUT_HEADER';
write;
$~ = 'STDOUT';

my($person,$data);

while( ($person,$data) = each %people ) {
    write;
}

$~ = 'STDOUT_FOOTER';
write;

format STDOUT_HEADER =
+--------------+-----+--------+--------+
| Name         | Age | Weight | Target |
+--------------+-----+--------+--------+
.

format STDOUT =
| @<<<<<<<<<<< | @>> |   @>>> |    @>> |
$person, $data->{age}, $data->{weight}, $data->{target}
.

format STDOUT_FOOTER =
+--------------+-----+--------+--------+
.

__DATA__
Name,Age,Weight,Target  
April,     23,    134,    90  
Jenna,     45,    156,    90  
Matt,      12,    90,     90  
Aaron,     34,    190,    90  
Daniel,    22,    188,    90

Вывод

$VAR1 = {
          'Daniel' => {
                        'weight' => '188',
                        'age' => '22',
                        'target' => '90'
                      },
          'April' => {
                       'target' => '90',
                       'age' => '23',
                       'weight' => '134'
                     },
          'Aaron' => {
                       'target' => '90',
                       'age' => '34',
                       'weight' => '190'
                     },
          'Matt' => {
                      'weight' => '90',
                      'age' => '12',
                      'target' => '90'
                    },
          'Jenna' => {
                       'target' => '90',
                       'age' => '45',
                       'weight' => '156'
                     }
        };


+--------------+-----+--------+--------+
| Name         | Age | Weight | Target |
+--------------+-----+--------+--------+
| Aaron        |  34 |    190 |     90 |
| Jenna        |  45 |    156 |     90 |
| Daniel       |  22 |    188 |     90 |
| Matt         |  12 |     90 |     90 |
| April        |  23 |    134 |     90 |
+--------------+-----+--------+--------+
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...