你原来的问题有很多缺陷。在提出任何改进建议之前应解决这些问题:
- 始终在每个脚本的顶部添加
use strict; 和 use warnings;。
- 您的
or die "!S" 语句已损坏。错误代码实际上在$! 中。但是,您可以通过 use autodie; 跳过这样做的需要
- 为您的文件句柄提供更有意义的名称。
$Fh 和 $F 只字不提它们的用途。至少将它们标记为$infh 和$outfh。
-
while (my @line = <$Fh>) { 有缺陷,因为它可以简化为my @line = <$Fh>;。因为您要在列表上下文中进行 readline ,所以它将吞下整个文件,并且下一个循环它将退出。相反,将其分配给一个标量,您甚至不需要下一个 for 循环。
- 如果您想将整个文件放入
@line,那么您对for(my$i =0;$i<=13655;$i++){ 的使用也是有缺陷的。您应该迭代到@line 的最后一个索引,即$#line。
-
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;
}
}