Perlを使用して、正規表現の置換を使用して連続する一致の間に文字を散在させるにはどうすればよいですか?
-
22-07-2019 - |
質問
カンマ区切り値の次の行には、連続した空のフィールドがいくつか含まれています。
$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
私が望んでいたものではありません。この問題は、連続する3つ以上のコンマが発生したときに発生します。正規表現は一度に2つのコンマをむさぼり食うので、文字列を再スキャンするときは、2番目ではなく3番目のコンマから始まります。
これは先読みアサーションとルックバックアサーションに関係があると考えたため、次の正規表現を試しました:
$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
それも機能しませんでした。コンマペアを1つシフトしました。
この文字列を同じ正規表現で2回洗浄するとうまくいくことを知っていますが、それは粗雑なようです。確かに、ジョブを実行するために単一の正規表現置換を取得する方法が必要です。提案はありますか?
最終的な文字列は次のようになります。
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
解決
後読みの例であなたがやろうとしていたことを完全に理解することはできませんでしたが、そこで優先順位エラーが発生しているのではないかと思います。後読みの後はすべて(?: ...)
ので、 |
は後読みを行うことを避けません。
最初から始めて、あなたがやろうとしていることは非常に単純に聞こえます:カンマの後に別のコンマまたは改行が続く場合は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"
他のヒント
編集:データ文字列のファイルハンドルを開き、 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 編集:データ文字列のファイルハンドルを開き、 readline
で行末を処理できることに注意してください:
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;
次も使用できます。
$str =~ s{,(,|\n)}{,N/A$1}g;
説明: s ///
が ,,
を見つけ、それを、N / A、
に置き換えたとき、すでに最後のコンマの後の文字。そのため、使用するのが
のみの場合、連続するいくつかのコンマが欠落します。
$str =~ s!,(?=[,\n])!,N/A!g;
したがって、ループを使用して、置換が成功するたびに pos $ str
を文字ごとに戻しました。
今、 @ ysthショー:
<*>
while
は不要になります。
? 編集:データ文字列のファイルハンドルを開き、 readline
で行末を処理できることに注意してください:
<*>
出力:
<*>
次も使用できます。
<*>
説明: s ///
が ,,
を見つけ、それを、N / A、
に置き換えたとき、すでに最後のコンマの後の文字。そのため、使用するのが
のみの場合、連続するいくつかのコンマが欠落します。
<*>
したがって、ループを使用して、置換が成功するたびに pos $ str
を文字ごとに戻しました。
今、 @ ysthショー:
<*>
while
は不要になります。
: 'N/A'} split /,/, $row, -1
), "\n";
}
出力:
<*>次も使用できます。
<*>説明: s ///
が ,,
を見つけ、それを、N / A、
に置き換えたとき、すでに最後のコンマの後の文字。そのため、使用するのが
したがって、ループを使用して、置換が成功するたびに pos $ str
を文字ごとに戻しました。
今、 @ ysthショー:
<*> while
は不要になります。
検索できます
(?<=,)(?=,|$)
そしてそれをN / Aに置き換えます。
この正規表現は、2つのコンマの間、またはコンマと行末の間の(空の)スペースに一致します。
高速でダーティなハックバージョン:
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;
最速のコードではなく、最短のコード。最大で2回ループする必要があります。
正規表現ではないが、複雑すぎない:
$string = join ",", map{正規表現ではないが、複雑すぎない:
<*>
、-1
は、最後に split
に文字列の最後に空のフィールドを含めるために必要です。
eq "" ? "N/A" : 正規表現ではないが、複雑すぎない:
<*>
、-1
は、最後に split
に文字列の最後に空のフィールドを含めるために必要です。
} split (/,/, $string,-1);
、-1
は、最後に split
に文字列の最後に空のフィールドを含めるために必要です。