Wie verwende ich Perl Zeichen zwischen Spielen in Folge mit einer regex Substitution durchsetzen?

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

  •  22-07-2019
  •  | 
  •  

Frage

Die folgenden Zeilen von durch Kommas getrennte Werte enthält mehrere aufeinanderfolgende leere Felder:

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

Ich möchte diese leeren Felder ersetzen mit ‚N / A‘ Werte, weshalb ich es über eine regex Substitution zu tun beschlossen.

Ich habe versucht, dies vor allem:

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

, die zurückgegeben

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

Nicht das, was ich wollte. Das Problem tritt auf, wenn mehr als zwei aufeinander folgende Kommas auftreten. Die Regex verschlingt zwei Kommas zu einer Zeit, so dass es auf dem dritten Komma beginnt eher als die zweite, wenn es erneut prüft die Zeichenfolge.

dachte ich, das könnte etwas gegen Lookback Behauptungen mit Look-Ahead zu tun, also habe ich versucht, die folgende Regex aus:

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

, die in Folge:

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

Das ist auch nicht funktioniert. Es verlagert nur die Komma-Paarungen nach der anderen.

Ich weiß, dass diese Zeichenfolge durch die gleiche Regex zweimaligem Waschen wird es tun, aber das scheint roh. Sicherlich muss es eine Möglichkeit sein, eine einzige regex Substitution zu bekommen den Job zu erledigen. Irgendwelche Vorschläge?

Der letzte Zeichenfolge sollte wie folgt aussehen:

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
War es hilfreich?

Lösung

Ich kann nicht ganz verstehen, was Sie in Ihrem Beispiel Lookbehind vorhatten, aber ich vermute, dass Sie von einem Vorrang Fehler leiden dort, und dass alles, was nach dem Lookbehind sollte in einem (?: ... ) eingeschlossen werden, so dass die | doesn‘ t vermeiden, dass die Lookbehind zu tun.

Starten von Grund auf, was Sie versuchen, klingt zu tun ist ziemlich einfach: Ort N / A nach einem Komma, wenn es von einem anderen Komma oder eine neue Zeile folgt:

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

Beispiel:

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

Ausgabe:

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

Andere Tipps

EDIT: Beachten Sie, dass Sie einen Dateihandle der Datenkette öffnen können und readline Deal mit Zeilenenden lassen:

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

Ausgabe:

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

Sie können auch verwendet werden:

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

Erklärung: Wenn s/// ein ,, findet und ersetzt sie durch ,N/A, hat es bereits auf die Zeichen nach dem letzten Komma verschoben. So wird es einige aufeinander folgende Kommas verpassen, wenn Sie nur verwenden

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

Daher verwenden ich eine Schleife zurück pos $str durch ein Zeichen nach jeder erfolgreichen Substitution zu bewegen.

Jetzt, da @ ysth zeigt :

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

würde die while überflüssig machen.

Sie können für die Suche

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

und ersetzen Sie, dass mit N / A.

Diese Regex entspricht den (leeren) Raum zwischen zwei Kommas oder zwischen einem Komma und Zeilenende.

Die quick and dirty Hack Version:

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;

Nicht der schnellste Code, aber die kürzeste. Es sollte zweimal bei max Schleife durch.

Nicht ein regulärer Ausdruck, aber nicht zu kompliziert entweder:

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

Die ,-1 am Ende benötigt werden split zu zwingen, alle leere Felder am Ende der Zeichenfolge enthalten.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top