读取带有不均匀逗号但固定列数的CSV文件

时间:2016-06-13 16:11:52

标签: perl csv perl-data-structures

我希望能够将此CSV文件读入数组或散列数组以进行操作。我怎么能去做呢?

例如,我的文件包含以下内容(第一行是标题):

Name,Age,Items,Available
John,29,laptop,mouse,Yes
Jane,28,desktop,keyboard,mouse,yes
Doe,56,tablet,keyboard,trackpad,touchpen,Yes

第一列是名称,第二列是年龄,第三列是项目,但项目可以包含多个以逗号分隔的内容,最后一列是人员可用性。

我怎样才能准确地读到这个?

3 个答案:

答案 0 :(得分:5)

格式正确的CSV引用包含逗号作为值的一部分的字段。如果您的CSV格式正确,请使用Text::CSV模块:

use Text::CSV;

my $csv = Text::CSV->new();
while (my $row = $csv->getline(\*DATA)) {
    my $name      = $row->[0];
    my $age       = $row->[1];
    my @items     = split /,/, $row->[2];
    my $available = $row->[3];
    print "$name/$age/@items/$available\n";
}

__DATA__
Name,Age,Items,Available
John,29,"laptop,mouse",Yes
Jane,28,"desktop,keyboard,mouse",yes
Doe,56,"tablet,keyboard,trackpad",touchpen,Yes

输出:

Name/Age/Items/Available
John/29/laptop mouse/Yes
Jane/28/desktop keyboard mouse/yes
Doe/56/tablet keyboard trackpad touchpen/Yes

如果您的CSV格式不正确,您需要根据您的数据知识实施自定义解析。假设Items列是唯一的多值字段,您可以在逗号上拆分,然后删除具有已知位置的字段。无论剩下什么都是物品。

while (my $line = <DATA>) {
    chomp $line;
    my @record    = split /,/, $line;
    my $name      = shift @record;
    my $age       = shift @record;
    my $available = pop   @record;
    my @items     = @record;

    print "$name/$age/@items/$available\n";
}

__DATA__
Name,Age,Items,Available
John,29,laptop,mouse,Yes
Jane,28,desktop,keyboard,mouse,yes
Doe,56,tablet,keyboard,trackpad,touchpen,Yes

或者,您可以使用数组切片来获得相同的结果:

 my ($name, $age, $available, @items) = @record[0, 1, -1, 2 .. @record - 2];

答案 1 :(得分:2)

由于您的数据实际上是格式正确的CSV文件,因此您可以使用标准工具来读取和存储它

以下是我现在假设你有

的数据
Name,Age,Items,Available
John,29,"laptop,mouse",Yes
Jane,28,"desktop,keyboard,mouse",yes
Doe,56,"tablet,keyboard,trackpad,touch pen",Yes

解决方案

与我原来的答案一样,此代码使用Text::CSV来解析每行输入。但不必重新格式化,每行可以直接推到数组@data

与以前一样,它符合STDIN的阅读标准。但这次我使用Data::Dump来揭示已构建的内存数据结构。如果在命令行上运行它,则应使用

$ perl unpack_csv.pl text.csv
use strict;
use warnings 'all';

use Text::CSV;

my $csv = Text::CSV->new;

my @data;

while ( <> ) {
    $csv->parse($_);
    my @row = $csv->fields;
    push @data, \@row;
}

use Data::Dump;
dd \@data;

答案 2 :(得分:0)

更新

我现在意识到OP的文件可能包含格式正确的CSV数据,这使得这个答案变得多余

然而,问题并没有改变以显示真实数据,所以我在这里留下这个答案,以防问题的主题和内容引诱人们有一个问题 解决

我建议您使用中间程序正确格式化CSV文件。获得标准格式文件后,可以使用带有Text::CSV,Excel或类似内容的Perl处理生成的输出

此程序使用Text::CSV读取您的输入数据,并在必要时编写引号括起来的Items

它的工作原理是使用Text::CSV->parse将每一行拆分为字段,然后为新字段1,2和4保留前两个和最后一个字段。剩下的是用逗号,连接的并用于字段3.四个结果值将传递回Text::CSV->combine并打印

它符合从STDIN读取和写入STDOUT的标准,所以如果你在命令行上运行它应该使用

$ perl reformat_csv.pl text.csv > new_text.csv
use strict;
use warnings 'all';

use Text::CSV;

my $csv = Text::CSV->new;

while ( <> ) {

    $csv->parse($_);
    my @row = $csv->fields;

    my $f1 = shift @row;
    my $f2 = shift @row;
    my $f4 = pop @row;

    my $f3 = join ',', @row;

    $csv->combine($f1, $f2, $f3, $f4);
    print $csv->string, "\n";
}

输出

Name,Age,Items,Available
John,29,"laptop,mouse",Yes
Jane,28,"desktop,keyboard,mouse",yes
Doe,56,"tablet,keyboard,trackpad,touchpen",Yes