【问题标题】:Perl split() Function Not Handling Pipe Character Saved As A VariablePerl split() 函数不处理保存为变量的管道字符
【发布时间】:2011-07-11 20:22:00
【问题描述】:

我在使用 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);

【问题讨论】:

    标签: perl csv


    【解决方案1】:

    split 的第一个参数是编译的正则表达式或正则表达式模式。如果你想在 text | 上拆分。您需要传递一个与| 匹配的pattern

    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
    

    【讨论】:

    • 奥利?我不知道 quotemeta 功能!从现在开始,我一定会记得使用它。非常感谢您的回答!
    【解决方案2】:

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

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

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

    见:http://perldoc.perl.org/perlre.html

    【讨论】:

    • 双引号占用了\| 并将其更改为|。您需要一对反斜杠,以便双引号将反斜杠视为特殊的并将它们转换为单个反斜杠。当您在正则表达式中有多个字符要引用时,您的头脑会旋转,如果其中一个已经需要引用,那么祝您好运。最好让 perl 在正则表达式阶段为您完成。
    • 或使用qr$re = qr/\|/; split $re
    【解决方案3】:

    似乎您要做的就是计算标题中的字段,然后打印标题。我可以建议一些比使用 split 更简单的方法吗?

    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";
    

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

    【讨论】:

    • 我不确定这是否适用于我的情况,只是因为标题可以是任何东西。虽然我的示例过于简单化,但现实世界的用法将包括字母数字和下划线。不过,我很感谢您的回复,并且一定会记住这一点!
    • @Zach 在第二个示例中,您可以输入任何字符作为分隔符。如果 X 是您指定的分隔符,它将拉出[^X]。就像它说的那样,通过使用字符类,您无需担心转义元字符。
    【解决方案4】:
    #!/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';
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-10-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多