Perl split()函数不处理保存为变量的管道字符

时间:2011-07-11 20:22:01

标签: perl csv

我在使用Perl的内置分割功能时遇到了一些麻烦。我正在创建一个脚本,用于编辑CSV文件的第一行,该文件使用管道进行列分隔。以下是第一行:

KEY|H1|H2|H3

但是,当我运行脚本时,这是我收到的输出:

Col1|Col2|Col3|Col4|Col5|Col6|Col7|Col8|Col9|Col10|Col11|Col12|Col13|

我有一种感觉,Perl并不喜欢我使用变量实际进行拆分的事实,在这种情况下,变量是一个管道。当我用实际管道替换变量时,它按预期完美地工作。当使用管道定界时,即使传入变量,我怎么能正确分割线?此外,作为一个愚蠢的警告,我没有权限从CPAN安装外部模块,所以我必须坚持内置的功能和模块。

对于上下文,这是我的脚本的必要部分:

our $opt_h;
our $opt_f;
our $opt_d;

# Get user input - filename and delimiter
getopts("f:d:h");

if (defined($opt_h)) {
    &print_help;
    exit 0;
}

if (!defined($opt_f)) {
   $opt_f = &promptUser("Enter the Source file, for example /qa/data/testdata/prod.csv");
}

if (!defined($opt_d)) {
    $opt_d = "\|";
}

my $delimiter = "\|";
my $temp_file = $opt_f;
my @temp_file = split(/\./, $temp_file);
$temp_file = $temp_file[0]."_add-headers.".$temp_file[1];

open(source_file, "<", $opt_f) or die "Err opening $opt_f: $!";
open(temp_file, ">", $temp_file) or die "Error opening $temp_file: $!";

my $source_header = <source_file>;
my @source_header_columns = split(/${delimiter}/, $source_header);
chomp(@source_header_columns);

for (my $i=1; $i<=scalar(@source_header_columns); $i++) {
    print temp_file "Col$i";
    print temp_file "$delimiter";
}
print temp_file "\n";
while (my $line = <source_file>) {
    print temp_file "$line";
}

close(source_file);
close(temp_file);

4 个答案:

答案 0 :(得分:6)

split的第一个参数是编译的正则表达式或正则表达式模式。如果您要拆分文本 |。您需要传递与|匹配的模式

quotemeta从匹配该字符串的字符串创建模式。

my $delimiter = '|';
my $delimiter_pat = quotemeta($delimiter);
split $delimiter_pat

或者,quotemeta可以在双引号字符串等中以\Q..\E的形式访问。

my $delimiter = '|';
split /\Q$delimiter\E/

\E如果最后都可以省略。

my $delimiter = '|';
split /\Q$delimiter/

我提到split也接受编译的正则表达式。

my $delimiter = '|';
my $delimiter_re = qr/\Q$delimiter/;
split $delimiter_re

如果你不介意对正则表达式进行硬编码,那就像

一样
my $delimiter_re = qr/\|/;
split $delimiter_re

答案 1 :(得分:5)

首先,|在双引号内并不特殊。将$ delimiter设置为"|",然后确保稍后引用它将有效或可能将$ delimiter设置为"\\|"本身就可以。

其次,|在regex中是特殊的,所以你想在那里引用它。最安全的方法是让perl为您引用代码。使用正则表达式中的\Q...\E构造来标记要引用的数据。

my @source_header_columns = split(/\Q${delimiter}\E/, $source_header);

请参阅:http://perldoc.perl.org/perlre.html

答案 2 :(得分:1)

看起来你要做的就是计算标题中的字段,并打印标题。我可能会建议比使用拆分更简单的事情吗?

my $str="KEY|H1|H2|H3"; 
my $count=0; 
$str =~ s/\w+/"Col" . ++$count/eg; 
print "$str\n";

适用于大多数任何分隔符(字母数字和下划线除外),它还会保存$count中的字段数,以备日后需要时使用。

这是另一个版本。这个使用字符类括号来指定“除此之外的任何字符”,这只是定义分隔符的另一种方式。您可以从命令行指定分隔符。你也可以使用你的getopts,但我只使用了一个简单的shift

my $d = shift || '[^|]';
if ( $d !~ /^\[/ ) {
    $d = '[^' . $d . ']';
}
my $str="KEY|H1|H2|H3"; 
my $count=0; 
$str =~ s/$d+/"Col" . ++$count/eg; 
print "$str\n";

通过使用括号,您无需担心转义元字符。

答案 3 :(得分:0)

#!/usr/bin/perl
use Data::Dumper;
use strict;
my $delimeter="\\|";
my $string="A|B|C|DD|E";
my @arr=split(/$delimeter/,$string);
print Dumper(@arr)."\n";

输出:

$VAR1 = 'A';
$VAR2 = 'B';
$VAR3 = 'C';
$VAR4 = 'DD';
$VAR5 = 'E';

您似乎需要将定界符定义为\\ |