Regex 대체와 연속 일치 사이에 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'값으로 바꾸고 싶습니다. 그래서 Regex 대체를 통해 그것을하기로 결정했습니다.
먼저 이것을 시도했습니다.
$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
내가 원했던 것이 아닙니다. 문제는 두 개 이상의 연속 쉼표가 발생할 때 발생합니다. REGEX는 한 번에 두 개의 쉼표를 올리므로 끈을 구출 할 때 두 번째 쉼표가 아닌 세 번째 쉼표에서 시작합니다.
나는 이것이 Lookahead vs. Lookback Assertions와 관련이 있다고 생각했기 때문에 다음과 같은 재학을 시도했습니다.
$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
해결책
나는 당신이 당신의 외모 예에서 당신이 무엇을하려고했는지 알 수 없었지만, 당신이 우선 순위 오류로 고통 받고 있다고 생각합니다. (?: ... )
그래서 |
외관을 피하지 않습니다.
처음부터 시작하여, 당신이하려는 일은 매우 간단하게 들립니다 : 다른 쉼표 나 신약이 뒤 따르는 쉼표 후에 해당 없음 :
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 $_ ? $_ : '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///
a ,,
그리고 그것을 대체합니다 ,N/A,
그것은 이미 마지막 쉼표 이후 캐릭터로 이동했습니다. 따라서 사용하면 연속 쉼표를 놓칠 것입니다.
$str =~ s{,(,|\n)}{,N/A$1}g;
따라서 루프를 사용하여 이동했습니다 pos $str
각각의 성공적인 대체 후 캐릭터에 의해 돌아갑니다.
이제 @ysth 쇼:
$str =~ s!,(?=[,\n])!,N/A!g;
할 것입니다 while
불필요한.
당신은 검색 할 수 있습니다
(?<=,)(?=,|$)
그리고 그것을 해당 없음으로 바꾸십시오.
이 REGEX는 두 쉼표 사이 또는 쉼표와 라인 끝 사이의 (빈) 공간과 일치합니다.
빠르고 더러운 해킹 버전 :
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;
가장 빠른 코드는 아니지만 가장 짧습니다. Max에서 두 번 루프해야합니다.
정수는 아니지만 너무 복잡하지는 않습니다.
$string = join ",", map{$_ eq "" ? "N/A" : $_} split (/,/, $string,-1);
그만큼 ,-1
힘이 끝날 때 필요합니다 split
문자열 끝에 빈 필드를 포함합니다.