在 Perl 中,我如何解析 space 分隔的数据,其中还包含一个带有 space 的字段?
In Perl, how can I parse space delimited data which also include a field with spaces?
当我运行下面的代码时,我得到了错误"Use of uninitialized value in subroutine entry at ./test.pl line 20."。
输入
2015-05-01 abc serv1 X View impl details 34 33 2 0 1 0 4552 3312 0 72 0 0 0 0 0 0 0
0 1 576 3 1 0 0 0 0 0 0 0 0 0.0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 381 671 1
2015-05-01 def serv2 X Assessment for next exam preview 22 22 0 0 1 0 1195 3577 0 3053 0 0 0 2 2 0
0 0 26 163 10 2 0 0 0 0 0 0 0 0 0.0 0 0 0 0 0 0 0 0 0 0 0
0 0 12 5 21 1
输出
前 4 个由 space 分隔的字段必须按原样打印。但是,如您所见,在 wards 的第 5 个字段中,可以有任意数量的 space 分隔词。我想将它们组合在一起作为第 5 个字段,直到找到一个数字作为下一个字段。在上面的示例输入中,我希望 "View impl details" 作为第 5 个字段,而不是 "view" 作为第 5 个,"impl" 作为第 6 个,细节作为第 7 个字段。第二行数据也是如此。我希望 "Assessment for next exam preview" 显示为第 5 个字段,其余显示为它们自己的字段。
#!/usr/bin/perl
use strict;
use warnings;
use POSIX;
my $i_file='../out/test.out';
my $o_file='../sql/test.out';
my $text_cont="";
open (FILE, $i_file) or die "Could not read from $i_file, program halting.";
while(<FILE>) {
(my $fl_1, my $fl_2, my $fl_3, my $fl_4, my @subfields) = split;
my @join_fields;
my $l=0;
for (my $k=5; $k <= 53; $k++) {
$join_fields[$l] = "";
if(isdigit($subfields[$k])) {
$join_fields[$l] = $subfields[$k];
$l = $l + 1;
}
else {
$join_fields[$l] = $join_fields[$l] . $subfields[$k];
}
}
}
close FILE;
我想从文件中读取数千行,每行包含由 space 分隔的 50 多个字段。我正在阅读每一行,将数据按 space 作为分隔符开始。从 wards 的第 5 个字段开始,直到我得到一个带有数字的字段,我想将这些字段附加到第 5 个字段。然后最后打印出输出。
我是 Perl 新手。我对该错误的理解是无法找到 "isdigit" 的定义。但是,在互联网上查看了一些解决方案,我使用了 POSIX 包。这似乎没有帮助。有人可以帮我实现我的要求吗?
更新脚本
#!/usr/bin/perl
use strict;
use warnings;
my $i_file='../out/test.out';
my $o_file='../sql/test.sql';
my $text_cont=" ";
open (FILE, $i_file) or die "Could not read from $i_file, program halting.";
while(<FILE>) {
(my $fl_1, my $fl_2, my $fl_3, my $fl_4, my @subfields) = split;
my @join_fields;
my $l=0;
foreach my $k_val ( @subfields ) {
#$join_fields[$l] = "";
if ($k_val ne " ") {
if ( $k_val =~ m/^\d+$/ ) {
$join_fields[$l] = $k_val;
$l = $l ++;
}
else {
my $temp = $join_fields[$l];
my $new_val = $temp.$k_val;
$join_fields[$l] = $new_val;
}
$text_cont = $text_cont."$join_fields[0]";
}
}
}
close FILE;
open STDOUT, ">", $o_file or die "[=13=]: open: $!";
print "$text_cont";
close STDOUT;
没有你的来源信息,我无法确定,但我认为你这里可能有一个围栏 post 错误:
(my $fl_1, my $fl_2, my $fl_3, my $fl_4, my @subfields) = split;
for (my $k=5; $k <= 53; $k++) {
if(isdigit($subfields[$k])) {
您正在将 @subfields 从 5 迭代到 53。但是第一个 'subfield' 字段是列表中的“第 4 个”字段。除非你真的是来自字段 9-57
。
我不认为你这样做,因为即使你去掉样本行上的 'wrapping' - 你的 'subfields' 只有 51 个元素。这是你问题的根源。
您还应该注意 split
在任何空白处拆分。
因此你得到 @subfields
包含:
$VAR1 = [
'View',
'impl',
'details',
'34',
'33',
'2',
但我建议您可能不想这样做 - 您只使用 $k
来索引 @subfields
。
那么为什么不改为:
foreach my $k_val ( @subfields ) {
if ( isdigit $k_val ) {
# etc...
}
}
但你也是对的 - 我收到警告说 isdigit
已被弃用:
Deprecated function whose use raises a warning, and which is slated to be removed in a future Perl version. It is very similar to matching against qr/ ^ [[:digit:]]+ $ /x , which you should convert to use instead.
有多种方法可以做类似的事情 - 我建议您可能想要:
if ( $k_val =~ m/^\d+$/ ) {
这将使用正则表达式来检查 $k_val
是否只是数字(1 个或多个数字字符)。
根据我对您的要求的理解,我已经修改了您的脚本。我已将输入记录分隔符 $/
从 \n
修改为 2015
,因为您需要处理的字符串由换行符分隔,虽然该解决方案非常 hackish,但它会工作:
我建议您检查 File::Stream 使输入记录分隔符 $/
成为正则表达式,即如果值不是 2015
或其他东西。
#!/usr/bin/perl
use strict;
use warnings;
local $/="2015"; # set input record separator as 2015
open my $fh, '<','file' or die "unable to open file: $! \n";
my @subfields;
my $junk=<$fh>; # remove first one
while(<$fh>){
chomp; # remove 2015 from last
$_= $junk.$_; # concatenate 2015 at begining of $_
(my $fl_1, my $fl_2, my $fl_3, my $fl_4, my @subfields) = split;
my @join_fields;
my $new_val="";
foreach my $k_val ( @subfields ) {
if ( $k_val =~ m/^\d+(.\d+)?$/ ) {
push(@join_fields,$k_val);
}
else{
$new_val .= $k_val;
}
}
push(@join_fields,$new_val);
my $fl_5 = pop @join_fields; # pop out your fifth field here
print "$fl_1 $fl_2 $fl_3 $fl_4 $fl_5 @join_fields \n";
}
close($fh);
如果这些确实是在复制和粘贴过程中被破坏的固定宽度字段,您应该使用 unpack
。否则,您可以利用 specify a limit when using split
:
If LIMIT
is specified and positive, it represents the maximum number of fields into which the EXPR
may be split; in other words, LIMIT
is one greater than the maximum number of times EXPR
may be split.
问题的原始措辞似乎暗示下面称为 $msg
的第五个字段从未包含数字。鉴于 OP 的评论显示至少有一行字段包含文本 WD25
,我正在更新下面的模式,以便对该字段中的文本更加宽松。
#!/usr/bin/env perl
use strict;
use warnings;
my $i_file = 'userpf.input';
open my $IN, '<', $i_file
or die "Cannot open '$i_file': $!";
my @data;
while (my $line = <$IN>) {
next unless $line =~ /\S/;
my ($date, $type, $serv, $flag, $rest) = split ' ', $line, 5;
my ($msg, $fields) = ($rest =~ /^ (.+?) \s+ ([0-9] .+) /x);
push @data, [ $date, $type, $serv, $flag, $msg, split(' ', $fields) ];
}
for my $x (@data) {
print "'$_'\n" for @$x;
}
我冒昧地给初始字段命名。
当我运行下面的代码时,我得到了错误"Use of uninitialized value in subroutine entry at ./test.pl line 20."。
输入
2015-05-01 abc serv1 X View impl details 34 33 2 0 1 0 4552 3312 0 72 0 0 0 0 0 0 0
0 1 576 3 1 0 0 0 0 0 0 0 0 0.0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 381 671 1
2015-05-01 def serv2 X Assessment for next exam preview 22 22 0 0 1 0 1195 3577 0 3053 0 0 0 2 2 0
0 0 26 163 10 2 0 0 0 0 0 0 0 0 0.0 0 0 0 0 0 0 0 0 0 0 0
0 0 12 5 21 1
输出
前 4 个由 space 分隔的字段必须按原样打印。但是,如您所见,在 wards 的第 5 个字段中,可以有任意数量的 space 分隔词。我想将它们组合在一起作为第 5 个字段,直到找到一个数字作为下一个字段。在上面的示例输入中,我希望 "View impl details" 作为第 5 个字段,而不是 "view" 作为第 5 个,"impl" 作为第 6 个,细节作为第 7 个字段。第二行数据也是如此。我希望 "Assessment for next exam preview" 显示为第 5 个字段,其余显示为它们自己的字段。
#!/usr/bin/perl
use strict;
use warnings;
use POSIX;
my $i_file='../out/test.out';
my $o_file='../sql/test.out';
my $text_cont="";
open (FILE, $i_file) or die "Could not read from $i_file, program halting.";
while(<FILE>) {
(my $fl_1, my $fl_2, my $fl_3, my $fl_4, my @subfields) = split;
my @join_fields;
my $l=0;
for (my $k=5; $k <= 53; $k++) {
$join_fields[$l] = "";
if(isdigit($subfields[$k])) {
$join_fields[$l] = $subfields[$k];
$l = $l + 1;
}
else {
$join_fields[$l] = $join_fields[$l] . $subfields[$k];
}
}
}
close FILE;
我想从文件中读取数千行,每行包含由 space 分隔的 50 多个字段。我正在阅读每一行,将数据按 space 作为分隔符开始。从 wards 的第 5 个字段开始,直到我得到一个带有数字的字段,我想将这些字段附加到第 5 个字段。然后最后打印出输出。
我是 Perl 新手。我对该错误的理解是无法找到 "isdigit" 的定义。但是,在互联网上查看了一些解决方案,我使用了 POSIX 包。这似乎没有帮助。有人可以帮我实现我的要求吗?
更新脚本
#!/usr/bin/perl
use strict;
use warnings;
my $i_file='../out/test.out';
my $o_file='../sql/test.sql';
my $text_cont=" ";
open (FILE, $i_file) or die "Could not read from $i_file, program halting.";
while(<FILE>) {
(my $fl_1, my $fl_2, my $fl_3, my $fl_4, my @subfields) = split;
my @join_fields;
my $l=0;
foreach my $k_val ( @subfields ) {
#$join_fields[$l] = "";
if ($k_val ne " ") {
if ( $k_val =~ m/^\d+$/ ) {
$join_fields[$l] = $k_val;
$l = $l ++;
}
else {
my $temp = $join_fields[$l];
my $new_val = $temp.$k_val;
$join_fields[$l] = $new_val;
}
$text_cont = $text_cont."$join_fields[0]";
}
}
}
close FILE;
open STDOUT, ">", $o_file or die "[=13=]: open: $!";
print "$text_cont";
close STDOUT;
没有你的来源信息,我无法确定,但我认为你这里可能有一个围栏 post 错误:
(my $fl_1, my $fl_2, my $fl_3, my $fl_4, my @subfields) = split;
for (my $k=5; $k <= 53; $k++) {
if(isdigit($subfields[$k])) {
您正在将 @subfields 从 5 迭代到 53。但是第一个 'subfield' 字段是列表中的“第 4 个”字段。除非你真的是来自字段 9-57
。
我不认为你这样做,因为即使你去掉样本行上的 'wrapping' - 你的 'subfields' 只有 51 个元素。这是你问题的根源。
您还应该注意 split
在任何空白处拆分。
因此你得到 @subfields
包含:
$VAR1 = [
'View',
'impl',
'details',
'34',
'33',
'2',
但我建议您可能不想这样做 - 您只使用 $k
来索引 @subfields
。
那么为什么不改为:
foreach my $k_val ( @subfields ) {
if ( isdigit $k_val ) {
# etc...
}
}
但你也是对的 - 我收到警告说 isdigit
已被弃用:
Deprecated function whose use raises a warning, and which is slated to be removed in a future Perl version. It is very similar to matching against qr/ ^ [[:digit:]]+ $ /x , which you should convert to use instead.
有多种方法可以做类似的事情 - 我建议您可能想要:
if ( $k_val =~ m/^\d+$/ ) {
这将使用正则表达式来检查 $k_val
是否只是数字(1 个或多个数字字符)。
根据我对您的要求的理解,我已经修改了您的脚本。我已将输入记录分隔符 $/
从 \n
修改为 2015
,因为您需要处理的字符串由换行符分隔,虽然该解决方案非常 hackish,但它会工作:
我建议您检查 File::Stream 使输入记录分隔符 $/
成为正则表达式,即如果值不是 2015
或其他东西。
#!/usr/bin/perl
use strict;
use warnings;
local $/="2015"; # set input record separator as 2015
open my $fh, '<','file' or die "unable to open file: $! \n";
my @subfields;
my $junk=<$fh>; # remove first one
while(<$fh>){
chomp; # remove 2015 from last
$_= $junk.$_; # concatenate 2015 at begining of $_
(my $fl_1, my $fl_2, my $fl_3, my $fl_4, my @subfields) = split;
my @join_fields;
my $new_val="";
foreach my $k_val ( @subfields ) {
if ( $k_val =~ m/^\d+(.\d+)?$/ ) {
push(@join_fields,$k_val);
}
else{
$new_val .= $k_val;
}
}
push(@join_fields,$new_val);
my $fl_5 = pop @join_fields; # pop out your fifth field here
print "$fl_1 $fl_2 $fl_3 $fl_4 $fl_5 @join_fields \n";
}
close($fh);
如果这些确实是在复制和粘贴过程中被破坏的固定宽度字段,您应该使用 unpack
。否则,您可以利用 specify a limit when using split
:
If
LIMIT
is specified and positive, it represents the maximum number of fields into which theEXPR
may be split; in other words,LIMIT
is one greater than the maximum number of timesEXPR
may be split.
问题的原始措辞似乎暗示下面称为 $msg
的第五个字段从未包含数字。鉴于 OP 的评论显示至少有一行字段包含文本 WD25
,我正在更新下面的模式,以便对该字段中的文本更加宽松。
#!/usr/bin/env perl
use strict;
use warnings;
my $i_file = 'userpf.input';
open my $IN, '<', $i_file
or die "Cannot open '$i_file': $!";
my @data;
while (my $line = <$IN>) {
next unless $line =~ /\S/;
my ($date, $type, $serv, $flag, $rest) = split ' ', $line, 5;
my ($msg, $fields) = ($rest =~ /^ (.+?) \s+ ([0-9] .+) /x);
push @data, [ $date, $type, $serv, $flag, $msg, split(' ', $fields) ];
}
for my $x (@data) {
print "'$_'\n" for @$x;
}
我冒昧地给初始字段命名。