【问题标题】:Extract data from file从文件中提取数据
【发布时间】:2014-05-01 09:18:49
【问题描述】:

我有类似的数据

"scott
E -45  COLLEGE LANE
BENGALI MARKET
xyz  -785698."
"Tomm
D.No: 4318/3,Ansari Road, Dariya Gunj,
xbc - 289235."

我编写了一个 Perl 程序来提取名称,即;

open(my$Fh, '<', 'printable address.txt') or die "!S";
open(my$F, '>', 'names.csv') or die "!S";
while (my@line =<$Fh> ) {
    for(my$i =0;$i<=13655;$i++){
        if ($line[$i]=~/^"/) {
        print $F $line[$i];
        }

    }
}

它工作正常,它可以准确地提取名称。现在我的目标是提取类似的地址

BENGALI MARKET
xyz  -785698."
D.No: 4318/3,Ansari Road, Dariya Gunj,
xbc - 289235."

在 CSV 文件中。请告诉我该怎么做

【问题讨论】:

    标签: perl csv text extract


    【解决方案1】:

    你原来的问题有很多缺陷。在提出任何改进建议之前应解决这些问题:

    1. 始终在每个脚本的顶部添加 use strict;use warnings;
    2. 您的or die "!S" 语句已损坏。错误代码实际上在$! 中。但是,您可以通过 use autodie; 跳过这样做的需要
    3. 为您的文件句柄提供更有意义的名称。 $Fh$F 只字不提它们的用途。至少将它们标记为$infh$outfh
    4. while (my @line = &lt;$Fh&gt;) { 有缺陷,因为它可以简化为my @line = &lt;$Fh&gt;;。因为您要在列表上下文中进行 readline ,所以它将吞下整个文件,并且下一个循环它将退出。相反,将其分配给一个标量,您甚至不需要下一个 for 循环。
    5. 如果您想将整个文件放入@line,那么您对for(my$i =0;$i&lt;=13655;$i++){ 的使用也是有缺陷的。您应该迭代到@line 的最后一个索引,即$#line
    6. if ($line[$i]=~/^"/) { 也有缺陷,因为您将引号字符 " 留在您要匹配的名称的开头。而是添加一个捕获组来提取名称。

    根据建议的更改,代码简化为:

    use strict;
    use warnings;
    use autodie;
    
    open my $infh, '<', 'printable address.txt';
    open my $outfh, '>', 'names.csv';
    
    while (my $line = <$infh>) {
        if ($line =~ /^"(.*)/) {
            print $outfh "$1\n";
        }
    }
    

    现在,如果您还想隔离地址,可以使用与名称类似的方法。我将假设您可能希望将整个地址构建在一个变量中,这样您就可以用它做一些比盲目地将它们扔到文件中更复杂的事情。但是,现在镜像文件设置:

    use strict;
    use warnings;
    use autodie;
    
    open my $infh, '<', 'printable address.txt';
    open my $namefh, '>', 'names.csv';
    open my $addressfh, '>', 'address.dat';
    
    my $address = '';
    
    while (my $line = <$infh>) {
        if ($line =~ /^"(.*)/) {
            print $namefh "$1\n";
    
        } elsif ($line =~ /(.*)"$/) {
            $address .= $1;
            print $addressfh "$address\n";
            $address = '';
    
        } else {
            $address .= $line;
        }
    }
    

    最终,无论您想将数据用于什么用途,最好的解决方案可能是使用Text::CSV 将其输出到真实的 CSV 文件。这样它就可以很容易地导入到电子表格或其他系统中,而您不必再次解析它。

    use strict;
    use warnings;
    use autodie;
    
    use Text::CSV;
    
    my $csv = Text::CSV->new ( { binary => 1, eol => "\n" } ) 
        or die "Cannot use CSV: ".Text::CSV->error_diag ();
    
    open my $infh, '<', 'printable address.txt';
    open my $outfh, '>', 'address.csv';
    
    my @data;
    
    while (my $line = <$infh>) {
        # Name Field
        if ($line =~ /^"(.*)/) {
            @data = ($1, '');
    
        # End of Address        
        } elsif ($line =~ /(.*)"$/) {
            $data[1] .= $1;
            $csv->print($outfh, \@data);
    
        # Address lines     
        } else {
            $data[1] .= $line;
        }
    }
    

    【讨论】:

    • 显示一些错误。 Global symbol "@line" requires explicit package name at name.pl line 8. Global symbol "$i" requires explicit package name at name.pl line 8. Execution of name.pl aborted due to compilation errors.
    • @user3341177 是的,错过了删除对旧数组的引用。固定。
    • 该链接不添加任何信息。这与您在问题中发布的相同。
    • 专家您好,您的代码显示一些错误Global symbol "$outfh" requires explicit package name at name.pl line 15. Global symbol "@line" requires explicit package name at name.pl line 15. Global symbol "$i" requires explicit package name at name.pl line 15. Execution of name.pl aborted due to compilation errors.
    • 进行了最后一次更新,向您展示了如何使用实际的 CSV 解析器将数据写入单个文件,以便轻松导入电子表格或其他程序。
    猜你喜欢
    • 2020-12-20
    • 2017-04-03
    • 2013-07-28
    • 2021-08-02
    • 2013-03-12
    • 2013-03-20
    • 2019-01-22
    • 1970-01-01
    相关资源
    最近更新 更多