Como posso usar Perl para personagens Intercale entre jogos consecutivos com uma substituição regex?

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

  •  22-07-2019
  •  | 
  •  

Pergunta

As seguintes linhas de valores separados por vírgulas contém vários campos vazios consecutivos:

$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"

Eu quero substituir esses campos vazios com valores 'N / A', que é por isso que eu decidi fazê-lo através de uma substituição regex.

Eu tentei isso antes de tudo:

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

que retornou

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

Não é o que eu queria. O problema ocorre quando ocorrem mais de duas vírgulas consecutivas. Os GOBBLES regex até duas vírgulas de cada vez, por isso começa na terceira vírgula ao invés da segunda quando se examina novamente a corda.

Eu pensei que isso poderia ser algo a ver com a verificação à frente vs. lookback afirmações, então eu tentei o seguinte regex out:

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

que resultou em:

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

Isso não quer trabalhar. Ele só mudou a vírgula-pares por um.

Eu sei que lavar essa string através do mesmo regex duas vezes irá fazê-lo, mas que parece bruto. Certamente, deve haver uma maneira de obter uma única substituição regex para fazer o trabalho. Alguma sugestão?

A seqüência final deve ficar assim:

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
Foi útil?

Solução

Eu não conseguia entender o que você estava tentando fazer no seu exemplo lookbehind, mas eu suspeito que você está sofrendo de um erro de precedência lá, e que tudo após o lookbehind deve ser encerrado num (?: ... ) para que o doesn |' t Evite fazer o lookbehind.

Partindo do zero, o que você está tentando fazer sons bastante simples: coloque N / A após uma vírgula se for seguido por outro vírgula ou uma nova linha:

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

Exemplo:

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);

Output:

"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"

Outras dicas

EDIT: Note que você poderia abrir uma filehandle para a cadeia de dados e deixe negócio readline com fins de linha:

#!/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";
}

Output:

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

Você também pode usar:

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

Explicação: Quando s/// encontra um ,, e substitui-lo com ,N/A, já se mudou para o personagem após a última vírgula. Então, ele vai perder algumas vírgulas consecutivas, se você usar apenas

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

Por isso, eu usei um loop para mover pos $str volta por um personagem depois de cada substituição bem sucedido.

Agora, como @ ysth mostra :

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

faria o while desnecessário.

Você pode procurar

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

e substituir com que N / A.

Esta expressão regular corresponde ao espaço (vazio) entre duas ou vírgulas entre uma vírgula e de fim de linha.

A versão rápida e suja corte:

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;

Não é o código mais rápido, mas o mais curto. Ele deve percorrer no máximo duas vezes.

Não é um regex, mas não demasiado complicado ou:

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

O ,-1 é necessário no final a força split para incluir quaisquer campos vazios na parte final da cadeia.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top