Come posso usare il Perl per separare i caratteri tra partite consecutive con una sostituzione regex?

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

  •  22-07-2019
  •  | 
  •  

Domanda

Le seguenti righe di valori separati da virgola contengono diversi campi vuoti consecutivi:

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

Voglio sostituire questi campi vuoti con i valori 'N / A', motivo per cui ho deciso di farlo tramite una sostituzione regex.

L'ho provato prima di tutto:

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

che è tornato

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

Non quello che volevo. Il problema si verifica quando si verificano più di due virgole consecutive. Il regex divora due virgole alla volta, quindi inizia alla terza virgola anziché alla seconda quando riscatta la stringa.

Ho pensato che questo potesse avere a che fare con le affermazioni lookahead vs. lookback, quindi ho provato la seguente regex:

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

che ha prodotto:

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

Neanche quello ha funzionato. Ha appena spostato gli accoppiamenti virgola di uno.

So che lavare questa stringa con lo stesso regex due volte lo farà, ma sembra rozzo. Sicuramente, ci deve essere un modo per ottenere una singola sostituzione regex per fare il lavoro. Qualche suggerimento?

L'ultima stringa dovrebbe apparire così:

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
È stato utile?

Soluzione

Non riuscivo a capire cosa stavi cercando di fare nel tuo esempio di lookbehind, ma sospetto che tu stia soffrendo di un errore di precedenza lì e che tutto ciò che segue il lookbehind dovrebbe essere racchiuso in un (?: ...) quindi | non evita di guardare dietro.

A partire da zero, quello che stai cercando di fare sembra abbastanza semplice: inserisci N / A dopo una virgola se è seguita da un'altra virgola o da una nuova riga:

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

Esempio:

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"

Altri suggerimenti

EDIT: tieni presente che puoi aprire un filehandle nella stringa di dati e lasciare che readline gestisca le terminazioni di riga:

#!/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 

EDIT: tieni presente che puoi aprire un filehandle nella stringa di dati e lasciare che readline gestisca le terminazioni di riga:

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

Output:

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

Puoi anche usare:

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

Spiegazione: Quando s /// trova un ,, e lo sostituisce con , N / A, è già passato al carattere dopo l'ultima virgola. Quindi, mancheranno alcune virgole consecutive se usi solo

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

Pertanto, ho usato un ciclo per spostare pos $ str di un carattere dopo ogni sostituzione riuscita.

Ora, come @ spettacoli ysth :

<*>

renderebbe mentre inutili.

?

EDIT: tieni presente che puoi aprire un filehandle nella stringa di dati e lasciare che readline gestisca le terminazioni di riga:

<*>

Output:

<*>

Puoi anche usare:

<*>

Spiegazione: Quando s /// trova un ,, e lo sostituisce con , N / A, è già passato al carattere dopo l'ultima virgola. Quindi, mancheranno alcune virgole consecutive se usi solo

<*>

Pertanto, ho usato un ciclo per spostare pos $ str di un carattere dopo ogni sostituzione riuscita.

Ora, come @ spettacoli ysth :

<*>

renderebbe mentre inutili.

: 'N/A'} split /,/, $row, -1 ), "\n"; }

Output:

<*>

Puoi anche usare:

<*>

Spiegazione: Quando s /// trova un ,, e lo sostituisce con , N / A, è già passato al carattere dopo l'ultima virgola. Quindi, mancheranno alcune virgole consecutive se usi solo

<*>

Pertanto, ho usato un ciclo per spostare pos $ str di un carattere dopo ogni sostituzione riuscita.

Ora, come @ spettacoli ysth :

<*>

renderebbe mentre inutili.

Puoi cercare

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

e sostituiscilo con N / D.

Questa regex corrisponde allo spazio (vuoto) tra due virgole o tra una virgola e la fine della riga.

La versione hack veloce e sporca:

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;

Non il codice più veloce, ma il più breve. Dovrebbe essere ripetuto al massimo due volte.

Non è una regex, ma neanche troppo complicato:

$string = join ",", map{

Non è una regex, ma neanche troppo complicato:

<*>

, -1 è necessario alla fine per forzare split per includere tutti i campi vuoti alla fine della stringa.

eq "" ? "N/A" :

Non è una regex, ma neanche troppo complicato:

<*>

, -1 è necessario alla fine per forzare split per includere tutti i campi vuoti alla fine della stringa.

} split (/,/, $string,-1);

, -1 è necessario alla fine per forzare split per includere tutti i campi vuoti alla fine della stringa.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top