3

以下逗号分隔值行包含几个连续的空字段:

$rawData = 
"2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,,Clear\n
2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,,,\n"

我想用“N/A”值替换这些空字段,这就是为什么我决定通过正则表达式替换来做到这一点。

我首先尝试了这个:

$rawdata =~ s/,([,\n])/,N\/A/g; # RELABEL UNAVAILABLE DATA AS 'N/A'

返回

2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,N/A,Clear\n
2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,N/A,,N/A,\n

不是我想要的。当出现两个以上的连续逗号时会出现此问题。正则表达式一次吞噬两个逗号,因此它在重新扫描字符串时从第三个逗号而不是第二个逗号开始。

我认为这可能与前瞻与回溯断言有关,所以我尝试了以下正则表达式:

$rawdata =~ s/(?<=,)([,\n])|,([,\n])$/,N\/A$1/g; # RELABEL UNAVAILABLE DATA AS 'N/A'

这导致:

2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,,N/A,Clear\n
2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,N/A,,N/A,,N/A,,N/A\n

那也没有用。它只是将逗号配对移动了一个。

我知道通过相同的正则表达式清洗这个字符串两次就可以了,但这似乎很粗糙。当然,必须有一种方法可以让单个正则表达式替换来完成这项工作。有什么建议么?

最终字符串应如下所示:

2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,N/A,N/A,Clear\n
2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,N/A,,N/A,N/A,N/A,N/A,N/A\n
4

5 回答 5

3

编辑:请注意,您可以打开数据字符串的文件句柄并让readline处理行尾:

#!/usr/bin/perl

use strict; use warnings;
use autodie;

my $str = <<EO_DATA;
2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,,Clear
2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,,,
EO_DATA

open my $str_h, '<', \$str;

while(my $row = <$str_h>) {
    chomp $row;
    print join(',',
        map { length $_ ? $_ : 'N/A'} split /,/, $row, -1
    ), "\n";
}

输出:

E:\首页> t.pl
2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,N/A,清除
2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,N/A,N/A,N/A,N/A

您还可以使用:

pos $str -= 1 while $str =~ s{,(,|\n)}{,N/A$1}g;

解释:当s///找到 a,,并用它替换它时,,N/A,它已经移动到最后一个逗号之后的字符。所以,如果你只使用它会错过一些连续的逗号

$str =~ s{,(,|\n)}{,N/A$1}g;

因此,我使用循环pos $str在每次成功替换后向后移动一个字符。

现在,正如@ysth 所示

$str =~ s!,(?=[,\n])!,N/A!g;

会使while不必要的。

于 2009-10-29T19:54:05.977 回答
2

我无法完全弄清楚您在后​​视示例中要做什么,但我怀疑您在那里遇到了优先级错误,并且后视之后的所有内容都应该包含在 a 中(?: ... ),这样|就不会避免进行后视.

从头开始,您尝试做的事情听起来很简单:如果逗号后面跟着另一个逗号或换行符,则将 N/A 放在逗号后面:

s!,(?=[,\n])!,N/A!g;

例子:

my $rawData = "2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,,Clear\n2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,,,\n";

use Data::Dumper;
$Data::Dumper::Useqq = $Data::Dumper::Terse = 1;
print Dumper($rawData);
$rawData =~ s!,(?=[,\n])!,N/A!g;
print Dumper($rawData);

输出:

"2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,,Clear\n2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,,,\n"
"2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,N/A,Clear\n2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,N/A,N/A,N/A,N/A\n"
于 2009-10-29T20:12:40.910 回答
2

你可以搜索

(?<=,)(?=,|$)

并将其替换为 N/A。

此正则表达式匹配两个逗号之间或逗号和行尾之间的(空)空格。

于 2009-10-29T20:13:01.847 回答
1

快速而肮脏的黑客版本:

my $rawData = "2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,,Clear
2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,,,\n";
while ($rawData =~ s/,,/,N\/A,/g) {};
print $rawData;

不是最快的代码,而是最短的。它应该最多循环两次。

于 2009-10-29T20:10:57.893 回答
1

不是正则表达式,但也不是太复杂:

$string = join ",", map{$_ eq "" ? "N/A" : $_} split (/,/, $string,-1);

最后,-1需要 强制split在字符串末尾包含任何空字段。

于 2009-10-29T20:16:46.510 回答