我有一个CSV文件,我需要将每个值括在引号中,其中每个值都是一个字符串。连接时我得到了意想不到的引号
$outline = "";
$line = "John,Smith,jsmith@bogusaddress.net,000-0000";
@parts = split (',',$line);
for $part (@parts) {
$part = '"' . $part . '"';
if ($outline eq "") {
$outline = $part; # reconstruct line
} else {
$outline = $outline . "," . $part;
}
}
$outline = $outline . "," . '"' . $parts[0] . " " . $parts[1] . '"';
print "$outline\n";
我期待:
"John","Smith","jsmith.net","000-0000","John Smith"
但我得到了:
"John","Smith","jsmith.net","000-0000",""John" "Smith""
为什么我会得到额外的报价?
感谢您的帮助。
答案 0 :(得分:6)
已提供了许多实用的解决方案,但我想解决您的问题:为什么会这样?
您获得双引号的原因是您实际上正在更改@parts
的元素。在for
循环中,元素被赋予循环参数的别名,因此对它们的任何更改都直接在" real"价值观也是如此。请考虑以下事项:
my @foos = 1 .. 3;
for my $foo (@foos) {
$foo += 1;
}
print "@foos"; # prints 2 3 4
因此,当您在代码中更改$part
时,数组@parts
也会更改,并且变为这样(Data::Dumper
输出):
$VAR1 = [
'"John"',
'"Smith"',
'"jsmith@bogusaddress.net"',
'"000-0000"'
];
从那时起,如果没有先删除引号,就无法将字符串"John"
和"Smith"
放在一起。
我还使用Text::CSV
准备了一个解决方案,我看到ThisSuitIsBlackNot已经这样做了,所以你可以看一下his answer的实际解决方案。
对于更轻量级的解决方案,您可以使用Text::ParseWords
。这与Text::CSV
一样,具有处理引用分隔符的好处。
use Text::ParseWords;
my $line = 'John,Smith,jsmith@bogusaddress.net,000-0000';
my @parts = quotewords(",", 0, $line);
push @parts, "@parts[0,1]";
print join ",", map qq("$_"), @parts;
答案 1 :(得分:2)
在处理分隔数据时,我总是使用Text::CSV
。它允许您轻松更改分隔符,引用行为和转义字符,并处理包含分隔符的字段,这很难自行处理(尽管这不适用于您的示例)。
以下内容将引用文件input.csv
中的所有字段,并将结果写入STDOUT
:
#!/usr/bin/perl
use strict;
use warnings;
use Text::CSV;
my $csv = Text::CSV->new({
binary => 1,
auto_diag => 1,
always_quote => 1,
eol => $/
}) or die "Cannot use CSV: " . Text::CSV->error_diag;
open my $fh, '<', 'input.csv' or die "input.csv: $!";
while (my $row = $csv->getline($fh)) {
$csv->print(\*STDOUT, $row);
}
close $fh;
input.csv
John,Smith,jsmith@bogusaddress.net,000-0000
Jane,Doe,jdoe@bogusaddress.net,000-0000
输出
"John","Smith","jsmith@bogusaddress.net","000-0000"
"Jane","Doe","jdoe@bogusaddress.net","000-0000"
答案 2 :(得分:0)
$part
循环别名中 foreach
@parts
的每个元素。所以你实际上存储回数组,你用引号包装的字符串。
尝试使用Data::Dumper
并在每个循环的底部转储@parts
。
use Data::Dumper;
...
print Dumper( \@parts );
答案 3 :(得分:0)
没有理由使用for
循环将各个部分串在一起。如果您可以使用split
,则可以使用join
:
my $line = "John,Smith,jsmith@bogusaddress.net,000-0000";
my @parts = split /,/, $line; # Split the line on commas
my $new_line = join q(","), @parts; # Separate out the parts with quote-comma-quote
my $new_line = qq("$new_line"); # Add pre and post quotes
q(...)
是一个quote-like运算符,用作单引号。 qq(...)
是一个类似于引用的运算符,用作双引号。理解qq("$line")
和q(",")
而不是"\"$line"\"
或'","'
会更容易理解。
我使用加入与","
加入所有部分。它处理$new_line
中间的分隔,但不处理开始和结束引用。因此,我需要第二个命令行来添加前缀和后置引号。