簡體   English   中英

如何使用Perl在正則表達式替換的連續匹配之間散布字符?

[英]How do I use Perl to intersperse characters between consecutive matches with a regex substitution?

以下逗號分隔值行包含幾個連續的空字段:

$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

不是我想要的。 出現兩個以上連續逗號時會出現此問題。 正則表達式一次吞噬兩個逗號,所以它從第三個逗號開始,而不是第二個逗號重新掃描字符串時。

我認為這可能與lookahead vs. lookback斷言有關,所以我嘗試了以下正則表達式:

$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

編輯:請注意,您可以打開數據字符串的文件句柄,讓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:\Home> t.pl
2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,N/A,Clear
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///找到一個,,並用,N/A,替換它時,N/A,它已經移動到最后一個逗號后面的字符。 所以,如果你只使用它,它會遺漏一些連續的逗號

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

因此,在每次成功替換后,我使用循環將pos $str移回一個字符。

現在,正如@ysth所示

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

將使while不必要的。

我無法弄清楚你在后面的示例中想要做什么,但我懷疑你在那里遇到了一個優先級錯誤,並且在lookbehind之后的所有內容都應該包含在(?: ... )所以| 不會避免做后衛。

從頭開始,您嘗試做的事情聽起來很簡單:如果后面跟着另一個逗號或換行符,請在逗號后面放置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"

你可以搜索

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

並用N / A代替。

此正則表達式匹配兩個逗號之間或逗號和行尾之間的(空)空格。

快速而骯臟的黑客版本:

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;

不是最快的代碼,但最短的。 它應該最多循環兩次。

不是正則表達式,但也不是太復雜:

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

最后需要,-1 ,強制split以包括字符串末尾的任何空字段。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM