【问题标题】:Perl parse CSV file "fill" and "null" fieldsPerl 解析 CSV 文件“填充”和“空”字段
【发布时间】:2016-06-08 17:42:37
【问题描述】:

好的 - 我要发布我的整个剧本,因为我不这样做会受到责备 - 尽管上次我这样做时我因为发布整个剧本而受到责备。我只需要知道我最初询问的那一行是否有效。整个脚本(在其他部门给我他们的数据与我们最初告诉的完全不同之前,它工作得很好)在最后跟进

我正在解析并清理一个 CSV 文件,以便将其加载到 MySQL 表中。它是通过其他人的“批处理 Java 程序”通过表加载的,如果任何字段为空,则批处理文件会因错误而停止。

有人告诉我,只要任何记录中有空字段,就填入一个空格。像这样简单的事情会奏效吗?

if ( ! length $fields[2] ) { 
    $_ = ' ' for $fields[2];
}

有没有办法同时检查多个字段?或者更好的办法是检查所有字段(这是在拆分记录之后),这是我在将记录写回 CSV 文件之前做的最后一件事。

这是整个脚本。请不要告诉我我在已经工作的脚本中做的事情不是你会做的事情。 -

#!/usr/bin/perl/

use strict;
use warnings;
use Data::Dumper;
use Time::Piece;

my $filename = 'mistints_1505_comma.csv';
#my $filename = 'test.csv';

# Open input file
open my $FH, $filename
  or die "Could not read from $filename <$!>, program halting.";

# Open error handling file
open ( my $ERR_FH, '>', "errorFiles1505.csv" ) or die $!;

# Read the header line of the input file and print to screen.
chomp(my $line = <$FH>);
my @fields = split(/,/, $line);
print Dumper(@fields), $/;

my @data;

# Read the lines one by one.
while($line = <$FH>) {

    chomp($line);

# Scrub data of characters that cause scripting problems down the line.
    $line =~ s/[\'\\]/ /g;

# split the fields of each record

    my @fields = split(/,/, $line);

# Check if the storeNbr field is empty.  If so, write record to error file.
    if (!length $fields[28]) {
        chomp (@fields);
        my $str = join ',', @fields;
        print $ERR_FH "$str\n";
        }
    else
    {

# Concatenate the first three fields and add to the beginning of each record
    unshift @fields, join '_', @fields[28..30];

# Format the DATE fields for MySQL
    $_ = join '-', (split /\//)[2,0,1] for @fields[10,14,24,26];

# Scrub colons from the data
    $line =~ s/:/ /g;

# If Spectro_Model is "UNKNOWN", change
    if($fields[22] eq "UNKNOWN"){
        $_ = 'UNKNOW' for $fields[22];
        }

# If tran_date is blank, insert 0000-00-00
    if(!length $fields[10]){
        $_ = '0000-00-00' for $fields[10];
        }

# If init_tran_date is blank, insert 0000-00-00
    if(!length $fields[14]){
        $_ = '0000-00-00' for $fields[14];
        }

# If update_tran_date is blank, insert 0000-00-00
    if(!length $fields[24]){
        $_ = '0000-00-00' for $fields[24];
        }

# If cancel_date is blank, insert 0000-00-00
    if(!length $fields[26]){
        $_ = '0000-00-00' for $fields[26];
        }

# Format the PROD_NBR field by deleting any leading zeros before decimals.
    $fields[12] =~ s/^\s*0\././;

# put the records back
    push @data, \@fields;
}
}

close $FH;
close $ERR_FH;

print "Unsorted:\n", Dumper(@data); #, $/;

#Sort the clean files on Primary Key, initTranDate, updateTranDate, and updateTranTime
@data = sort {
    $a->[0] cmp $b->[0] ||
    $a->[14] cmp $b->[14] ||
    $a->[26] cmp $b->[26] ||
    $a->[27] cmp $b-> [27]
} @data;

#open my $OFH, '>', '/swpkg/shared/batch_processing/mistints/parsedMistints.csv';
open my $OFH, '>', '/swpkg/shared/batch_processing/mistints/cleaned1505.csv';
print $OFH join(',', @$_), $/ for @data;
close $OFH;

exit;

【问题讨论】:

  • 所以null 在您的上下文中是一个没有数据的逗号分隔字段?我想知道如果您将每个逗号更改为由 whilespace 括起来的逗号会发生什么。就像1,$s/,/ , /g 会在vi 中做的那样。这是否会过多地破坏它,或者它是否可以容忍填充?如果不用担心行首或行尾的null,只需一个空格键即可
  • @BigRedEO:你没有说你想做什么。您是在处理文件中以逗号分隔的原始文本行,还是将其拆分为数组,或者什么?你想要一个文字空间,还是一个被引号包围的空间?
  • @BigRedEO 我建议您仔细考虑从人们那里得到的关于您的问题的 cmets 和反馈,而不是感到沮丧。请感谢所有这些人确实为您付出了他们的时间、精力和专业知识。您可以通过尽力发布好的和清晰的问题来提供帮助(这有时并不容易)。
  • @BigRedEO 让我提出这个建议——也许你在那个 powershell 论坛上的问题更好(如果你对那种材料更满意,很容易发生)?如果你在这里没有得到直接的答案,那只能意味着问题不够清楚(这里重视简洁明了的答案)。我已经查看了您的问题以及您收到的回复。如果可能的话,从你那里得到答案的每个人都会把你的问题一网打尽。但是当你有一点时间重新阅读他们的 cmets 和解决方案时,你会在那里找到所有答案。
  • @BigRedEO: “上次我因为发布整个脚本而受到责备” 问题是,我们需要看到尽可能多的信息,而不必筛选通过一堆垃圾。不要忘记您在此处请求帮助——我们除了感谢之外什么都得不到——如果您不想麻烦发布您的数据或减少您的代码,以便它紧凑但仍然行为不端,那么很难为您提供帮助。 Powershell 问题可能与语法无关,建议重写的人正试图帮助您再次避免同样的问题

标签: perl parsing null


【解决方案1】:

据我所知,您已经用逗号 , 拆分了一条记录,并且您希望将所有空字符串字段更改为包含一个空格

我会写这个

use strict;
use warnings 'all';

my $record = 'a,b,c,,e,,g,,i,,k,,m,n,o,p,q,r,s,t';

my @fields = map { $_ eq "" ? ' ' : $_ } split /,/, $record;


use Data::Dump;
dd \@fields;

输出

[ "a", "b", "c", " ", "e", " ", "g", " ", "i", " ", "k", " ", "m" .. "t" ]

或者,如果您有一些字段需要设置为不同的值(如果它们为空),您可以设置一个默认值数组

看起来像这样。除了字段 10、11 和 12(0000-00-00)之外,所有@defaults 数组都设置为空格。这些是在记录拆分后拾取的

use strict;
use warnings 'all';

my @defaults = (' ') x 20;

$defaults[$_] = '0000-00-00' for 9, 10, 11;

my $record = 'a,b,c,,e,,g,,i,,k,,m,n,o,p,q,r,s,t';

my @fields = split /,/, $record;

for my $i ( 0 .. $#fields ) {
    $fields[$i] = $defaults[$i] if $fields[$i] eq '';
}


use Data::Dump;
dd \@fields;

输出

[ "a", "b", "c", " ", "e", " ", "g", " ", "i", "0000-00-00", "k", "0000-00-00", "m" .. "t" ]



看过你的完整程序后,我推荐这样的东西。如果您显示了输入数据的示例,那么我可以使用哈希来引用列名而不是数字,从而使其更具可读性

#!/usr/bin/perl/

use strict;
use warnings 'all';

use Data::Dumper;
use Time::Piece;

my $filename = 'mistints_1505_comma.csv';
#my $filename = 'test.csv';

open my $FH, $filename
        or die "Could not read from $filename <$!>, program halting.";

open( my $ERR_FH, '>', "errorFiles1505.csv" ) or die $!;

chomp( my $line = <$FH> );
my @fields = split /,/, $line;    #/
print Dumper( \@fields ), "\n";

my @data;

# Read the lines one by one.
while ( <$FH> ) {

    chomp;

    # Scrub data of characters that cause scripting problems down the line.
    tr/'\\/  /;                   #'

    my @fields = split /,/;       #/

    # Check if the storeNbr field is empty.  If so, write record to error file.

    if ( $fields[28] eq "" ) {
        my $str = join ',', @fields;
        print $ERR_FH "$str\n";
        next;
    }

    # Concatenate the first three fields and add to the beginning of each record
    unshift @fields, join '_', @fields[ 28 .. 30 ];

    # Format the DATE fields for MySQL
    $_ = join '-', ( split /\// )[ 2, 0, 1 ] for @fields[ 10, 14, 24, 26 ];

    # Scrub colons from the data
    tr/://d;                      #/

    my $i = 0;
    for ( @fields ) {

        # If "Spectro_Model" is "UNKNOWN" then change to "UNKNOW"
        if ( $i == 22 ) {
            $_ = 'UNKNOW' if $_ eq 'UNKNOWN';
        }

        # If a date field is blank then insert 0000-00-00
        elsif ( grep { $i == $_ } 10, 14, 24, 26 ) {
            $_ = '0000-00-00' if $_ eq "";
        }

        # Format the PROD_NBR field by deleting any leading zeros before decimals.
        elsif ( $i == 12 ) {
            s/^\s*0\././;
        }

        # Change all remaining empty fields to a single space
        else {
            $_ = ' ' if $_ eq "";
        }

        ++$i;
    }

    push @data, \@fields;
}

close $FH;
close $ERR_FH;

print "Unsorted:\n", Dumper(@data);    #, $/;

#Sort the clean files on Primary Key, initTranDate, updateTranDate, and updateTranTime
@data = sort {
    $a->[0] cmp $b->[0]   or
    $a->[14] cmp $b->[14] or
    $a->[26] cmp $b->[26] or
    $a->[27] cmp $b->[27]
} @data;

#open my $OFH, '>', '/swpkg/shared/batch_processing/mistints/parsedMistints.csv';
open my $OFH, '>', '/swpkg/shared/batch_processing/mistints/cleaned1505.csv' or die $!;
print $OFH join(',', @$_), $/ for @data;
close $OFH;

【讨论】:

  • 感谢您的耐心等待。您和 zdim 过去曾帮助过我,我认为您的回答对我有很大帮助。我的部分挫败感还源于被告知一件事并使用一组数据,结果却获得了与其他部门完全不同的一组数据,这让我的一切都失去了意义。
  • 差不多了 - 这部分不起作用 - # If tran_date is blank, insert 0000-00-00 elsif ( grep { $i == $_ } 10, 14, 24, 26 ) { $_ = '0000-00-00' if $_ eq ""; } 它没有将任何空白或空白日期字段更改为 0000-00-00。我想我最初尝试过这样的事情,它只会检查字段 10,如果它不是空白的,它将不理会其余部分。相反,我现在有空格。
  • @BigRedEO:我希望你对我们正在尝试做的事情有更好的态度?我们通常要求Minimal, Complete, and Verifiable example (MCVE),以便我们可以为您提供适当的帮助。但这需要提出问题的人实际做一些工作,而这很少能顺利进行。很遗憾,您认为我们的帮助不如 PowerShell 人员,但 Perl 确实是另一回事,问题可能在您的代码中的其他地方甚至在您的数据中很远
  • @BigRedEO:如果我们在您发布的内容中看不出任何问题,那么我们可以说“那里没有问题!”或者我们可以要求查看更多您的代码或数据。你更喜欢哪个?就目前而言,您还没有向我提供任何数据;所以你留下了一个使用幻数而不是列名的程序。您可以编写更好的代码,但我很困惑为什么您要停止在几乎无法工作的东西上
  • @BigRedEO:顺便说一句,如果你刚刚复制了我的解决方案并将其作为工作完成提交,那你为什么不欠我几天的工资,比如 300 美元?跨度>
【解决方案2】:

如果你在分裂成$fields之前这样做了,你应该能够做类似的事情

# assuming a CSV line is in $_
#pad null at start of line
s/^,/ ,/;

#pad nulls in the middle
s/,,/, ,/g;

#pad null at the end
s/,$/, /;

【讨论】:

  • 问题是我有几个字段,如果它们为空(!长度),那么我必须向它们添加一些内容而不是空格,例如“0000-00-00”一些日期字段和其他一些类似的项目。一旦我首先完成了所有这些必要的替换,然后我想检查任何其他长度为零的字段并放在一个空格中。
【解决方案3】:

不要尝试推出您自己的 CSV 解析代码。使用Text::CSV 或Text::CSV::Slurp。

使用 Text::CSV 你可以做类似的事情

$line   = $csv->string();             # get the combined string
$status  = $csv->parse($line);        # parse a CSV string into fields
@columns = map {defined $_ ? $_ : " "} $csv->fields(); # get the parsed fields

您确定要用空格替换空值吗?我会说如果该字段未定义,则它在 db 中应该为 NULL。

【讨论】:

  • split /,/ 如果数据中没有引用字段,则完全令人满意
  • @jira - 我没有任何类型的文本:CSV,也不会有。我的脚本已经很好而且很漂亮,并且可以与我得到的原始“测试”数据一起正常工作。然后我从另一个部门得到的实际数据是造成这个额外麻烦的原因。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-14
  • 2013-02-20
  • 2021-07-09
  • 2017-04-20
  • 2021-01-03
相关资源
最近更新 更多