Regex 대체와 연속 일치 사이에 Perl을 사용하여 문자를 산다가 어떻게하나요?

StackOverflow https://stackoverflow.com/questions/1646137

  •  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 문자열 끝에 빈 필드를 포함합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top