为什么我的 Text::CSV 代码在打印它们时会在空格处拆分值?
Why does my Text::CSV code split values on spaces when I print them?
我有以下使用 Text::CSV
的代码:
#!/usr/bin/perl
package main;
use strict;
use warnings;
use Text::CSV;
my $csv = Text::CSV -> new ({ binary => 0, eol => $/ });
open my $io, "<", $file or die "$file: $!";
while (my $row = $csv -> getline ($io))
{
my @fields = @$row;
while(my $field = <@fields>)
{
print $field."\n";
}
}
(改编自 Text::CSV
documentation)
当我在为 @fields
数组分配来自 @$row
的值后尝试循环遍历它时,它会分解空格上的值。例如
"FOO","BAR","IM FOO BAR'D"
结果为
FOO
BAR
IM
FOO
BAR'D
为什么会发生这种情况,我该如何解决?
尝试
for my $field (@fields)
而不是
while(my $field = <@fields>)
while
语句与您认为的不一样。相当于
while (my $field = glob "@fields")
这与您的意思完全不同。 (glob 打破了它在空格上的论点并尝试扩展通配符,匹配磁盘上的文件。你的论点没有任何通配符,所以它最终的意思与 split ' ', "@fields"
大致相同)。
问题出在你的第二个循环中:
while(my $field = <@fields>)
{
print $field."\n";
}
您可以使用 B::Deparse
:
查看实际情况
$ perl -MO=Deparse -e 'while (my $field = <@fields>) { print $field."\n" }'
use File::Glob ();
while (defined(my $field = glob(join($", @fields)))) {
do {
print $field . "\n"
};
}
-e syntax OK
让我们稍微分解一下:
join($", @fields)
将 @fields
的元素连接成一个字符串,由 $"
分隔(默认为单个 space)。所以如果 @fields
包含 FOO
、BAR
和 IM FOO BAR'D
,join
的结果将是
FOO BAR IM FOO BAR'D
现在,glob
做什么?来自 perldoc -f glob
:
In list context, returns a (possibly empty) list of filename expansions on the value of EXPR
such as the standard Unix shell /bin/csh
would do. In scalar context, glob iterates through such filename expansions, returning undef
when the list is exhausted. This is the internal function implementing the <*.c>
operator [...]
Note that glob splits its arguments on whitespace and treats each segment as separate pattern.
所以
glob("FOO BAR IM FOO BAR'D")
在标量上下文中将 return FOO
,然后是 BAR
,然后是 IM
,依此类推。
作为 ,将循环更改为类似这样的内容以修复:
foreach my $field (@fields)
{
print "$field\n";
}
或者更好的是,不是将 @$row
的内容复制到 @fields
并循环遍历 @fields
,而是直接循环遍历 @$row
:
foreach my $field (@$row) {
print "$field\n";
}
我有以下使用 Text::CSV
的代码:
#!/usr/bin/perl
package main;
use strict;
use warnings;
use Text::CSV;
my $csv = Text::CSV -> new ({ binary => 0, eol => $/ });
open my $io, "<", $file or die "$file: $!";
while (my $row = $csv -> getline ($io))
{
my @fields = @$row;
while(my $field = <@fields>)
{
print $field."\n";
}
}
(改编自 Text::CSV
documentation)
当我在为 @fields
数组分配来自 @$row
的值后尝试循环遍历它时,它会分解空格上的值。例如
"FOO","BAR","IM FOO BAR'D"
结果为
FOO
BAR
IM
FOO
BAR'D
为什么会发生这种情况,我该如何解决?
尝试
for my $field (@fields)
而不是
while(my $field = <@fields>)
while
语句与您认为的不一样。相当于
while (my $field = glob "@fields")
这与您的意思完全不同。 (glob 打破了它在空格上的论点并尝试扩展通配符,匹配磁盘上的文件。你的论点没有任何通配符,所以它最终的意思与 split ' ', "@fields"
大致相同)。
问题出在你的第二个循环中:
while(my $field = <@fields>)
{
print $field."\n";
}
您可以使用 B::Deparse
:
$ perl -MO=Deparse -e 'while (my $field = <@fields>) { print $field."\n" }'
use File::Glob ();
while (defined(my $field = glob(join($", @fields)))) {
do {
print $field . "\n"
};
}
-e syntax OK
让我们稍微分解一下:
join($", @fields)
将 @fields
的元素连接成一个字符串,由 $"
分隔(默认为单个 space)。所以如果 @fields
包含 FOO
、BAR
和 IM FOO BAR'D
,join
的结果将是
FOO BAR IM FOO BAR'D
现在,glob
做什么?来自 perldoc -f glob
:
In list context, returns a (possibly empty) list of filename expansions on the value of
EXPR
such as the standard Unix shell/bin/csh
would do. In scalar context, glob iterates through such filename expansions, returningundef
when the list is exhausted. This is the internal function implementing the<*.c>
operator [...]Note that glob splits its arguments on whitespace and treats each segment as separate pattern.
所以
glob("FOO BAR IM FOO BAR'D")
在标量上下文中将 return FOO
,然后是 BAR
,然后是 IM
,依此类推。
作为
foreach my $field (@fields)
{
print "$field\n";
}
或者更好的是,不是将 @$row
的内容复制到 @fields
并循环遍历 @fields
,而是直接循环遍历 @$row
:
foreach my $field (@$row) {
print "$field\n";
}