RegExp matcht nur 2. Alternative - ratlos

RegExp matcht nur 2. Alternative - ratlos

am 09.03.2006 14:48:29 von t_pot

Aus einer CSV-Datei sollen in einer Spalte alle Feldinhalte, in denen ein *?*
oder eine Ziffer plus Fragezeichen *n?* stehen, in ein anderes Feld
verschoben werden. Hmm, ich hab' länger nüx mit Perl gemacht und
übersehe vieleicht was entscheidendes: Ich krieg's einfach nicht
hin, daß beide Patterns gematcht werden. Hier erst mal ein kurzes
Beispiel:

"Anton";"Kanton";"3";"nur drei"
"Peter";"Pan";"?"
"Paul";"Panther";"?";"blafasel"
"Elli";"Pirelli";"1?"
"Lisa";"Pisa";"2?";"soweit mal"

Die *?* und *?n* sollen ins letzte Feld und dort ggf. an vorhandene
Anmerkungen angehängt werden.

Und hier der Code-Schnipsel:

# Datensatz in ARRAY
@test = split (/;/, $TheLine);
#
# "?" und "?n" in Anmerkung verschieben
if ($test[2] =~ m/^("(\?|\d{1}\?)")$/) {
$test[2] =~ s/$1//g;
$KK = $2;
print STDOUT "Die Variable 1 lautet $1\n";
print STDOUT "Die Variable 2 lautet $2\n";
print STDOUT "KK ist $KK\n";
#
if ($test[3] =~ m/^"(.+)"$/) {
$Anm = $1;
$test[3] = $Anm . ", KK-04:" . $KK;
$test[3] =~ s/^(.+)$/"$1"/;
}
else {$test[3] = "KK-04:" . $KK4;
$kunden[18] =~ s/^(.+)$/"$1"/;
}
}

Die Print-Statements sind nur zur Kontrolle drinne! Und zeigen schon
mal, daß die Variablen nur belegt sind, wenn der RegExp auf *?n* stößt.
Also sie sind dann: "?n" ?n ?n

Bei den *?* sind alle drei leer. *kopfkratz Das dürfte aber doch nur
passieren, wenn der RegExp /^"\d{1}\?$/ wäre (Stichwort: gieriges
matchen). Er soll aber doch /entweder-oder/ suchen.

Vollends ins Staunen komme ich, wenn ich mir den Output anschaue:

"Anton";"Kanton";"3";"nur drei"
"Peter";"Pan";?;KK-04:;;;;;;;;;;;;;;;
"Paul";"Panther";?;"blafasel, KK-04:"
"Elli";"Pirelli";"1?";KK-04:1?;;;;;;;;;;;;;;;
"Lisa";"Pisa";"2?";"soweit mal, KK-04:2?"

Die 1. Zeile passt. In der 2. wird einerseits das *?* übersehen,
andereseits werden aber die umschließenden *"* gestrippt. Wie das, wo
doch ausweislich des Print-Statements gar nüx drinne steht in der
Variablen? Und woher stammt die wundersame Semikolon-Vermehrung? Denn in
der 3. Zeile, wo schon Text im letzten Feld vorhanden war, passt es nun
plötzlich wieder im letzten Feld.

In Zeile 4 und 5 wird in der 3. Spalte auch nicht gelöscht, obwohl hier lt.
Print-Statement /gemacht/ wurde. Und die "" bleiben hier stehen.

Ansonsten dasselbe wie bei *?*: Ist schon Text im letzten Feld, passt es
- ansonsten wieder Semikolon-Salat.

Abschließende Bemerkung: Das *s/^(.+)$/"$1"/g* habe ich gemacht, weil
ich es bei Neuschreiben des Feldes nicht geschafft habe, die "" gleich
mitzusetzen.

Wo liegt hier mein Denkfehler?

TIA,

--
Thomas Pothmann
PGP-Key on demand

Re: RegExp matcht nur 2. Alternative - ratlos

am 09.03.2006 15:02:17 von t_pot

Thomas Pothmann wrote:

>Aus einer CSV-Datei sollen in einer Spalte alle Feldinhalte, in denen ein *?*
>oder eine Ziffer plus Fragezeichen *n?* stehen, in ein anderes Feld
>verschoben werden.

Sorry, zwei Schreibfehler im Posting:

Es soll heissen *gematcht* und nicht *gemacht*.

Und in der letzten Code-Zeile latürnich *$test[3]* und nicht
*$kunden[18]* Hab' das Mini-Beispiel aus dem Original-Script genommen
....

--
Thomas Pothmann
PGP-Key on demand

Re: RegExp matcht nur 2. Alternative - ratlos

am 09.03.2006 15:32:50 von Andreas Karrer

* Thomas Pothmann :

> "Anton";"Kanton";"3";"nur drei"
> "Peter";"Pan";"?"
> "Paul";"Panther";"?";"blafasel"
> "Elli";"Pirelli";"1?"
> "Lisa";"Pisa";"2?";"soweit mal"
>
> Und hier der Code-Schnipsel:
>
> # Datensatz in ARRAY
> @test = split (/;/, $TheLine);

Das ist gefährlich, ausser du bist dir ganz sicher, dass in deinen
Daten nie ein ; innerhalb von "..." vorkommt. Sonst benutzt du besser
ein CSV-Modul.

> if ($test[2] =~ m/^("(\?|\d{1}\?)")$/) {

Matcht bei den Zeilen 2 bis 5. Kann man einfacher haben:

/^("(\d?\?)")$/

> $test[2] =~ s/$1//g;

Hier verwendest du den *String*, der gerade gematcht hat, als Regex.
Das bewirkt leider nicht das, was du vermutlich willst, weil der String
Regex-Metazeichen enthält (das ?). Mit der "Peter-Pan"-Zeile wird
folgende Substitution ausgeführt:

$test[2] =~ s/"?"//g;

Das gelingt tatsächlich, es wird als das erste " auf "?" entfernt (das
"? matcht auf den Nullstring, das zweite " aus der Regex matcht auf das
erste " in "?").

Weil diese zweite Regex (die aus der Substitution) gematcht hat, werden
die Variablen $1, $2 aus dem ersten Match überschrieben. Weil die zweite
Regex keine Klammern enthält, sind $1 und $2 nun undefiniert.

Bei der Pirelli-Zeile lautet die Substitution

$test[2] =~ s/"1?"//g;

und diese Regex matcht nicht, die Substritution wird gar nicht
ausgeführt. Die Variablen $1 und $2 des ersten Matchings bleiben
deshalb erhalten und werden ausgegeben.


Du willst vermutlich einfach sowas wie:

if ($test[2] =~ s/^"(\d?\?)"$//) {
$KK = $1;
...

oder

if ($test[2] =~ m/^"(\d?\?)"$/) {
$KK = $1;
$test[2] ="";
...


Wenn du unbedingt deinen alten Ansatz behalten willst, musst du die
Match-Variablen $1 und $2 vor einem neuen Matching abspeichern und
quotemeta() bzw. \Q benutzen:

if ($test[2] =~ m/^("(\?|\d{1}\?)")$/) {
$varEins = $1;
$KK = $2;
$test[2] =~ s/\Q$1//g;
print STDOUT "Die Variable 1 lautet $varEins\n";
print STDOUT "Die Variable 2 lautet $KK\n";



- Andi

Re: RegExp matcht nur 2. Alternative - ratlos

am 09.03.2006 19:50:45 von t_pot

Andreas Karrer wrote:

Zunächst mal besten Dank für die superschnelle Hilfe! Also: Die
Datenbank ist erfolgreich umsortiert - auch in dem hier besprochenen
Punkt. :o)

>* Thomas Pothmann :

>> "Anton";"Kanton";"3";"nur drei"
>> "Peter";"Pan";"?"
>> "Paul";"Panther";"?";"blafasel"
>> "Elli";"Pirelli";"1?"
>> "Lisa";"Pisa";"2?";"soweit mal"

>> Und hier der Code-Schnipsel:
>>
>> # Datensatz in ARRAY
>> @test = split (/;/, $TheLine);

>Das ist gefährlich, ausser du bist dir ganz sicher, dass in deinen
>Daten nie ein ; innerhalb von "..." vorkommt. Sonst benutzt du besser
>ein CSV-Modul.

Ja ich weiß! Aber als non-alphanumerische Zeichen kommen in den Feldern
nur /./, /,/ und /-/ vor. Das mit dem CSV-Modul muss ich ich mir mal
ergooglen und ansehen. Danke für den Hinweis.

>Hier verwendest du den *String*, der gerade gematcht hat, als Regex.
>Das bewirkt leider nicht das, was du vermutlich willst, weil der String
>Regex-Metazeichen enthält (das ?).

Weia! Da habe ich vor lauter Wald die Bäume nicht gesehen... :o(

>Mit der "Peter-Pan"-Zeile wird folgende Substitution ausgeführt:

> $test[2] =~ s/"?"//g;

>Das gelingt tatsächlich, es wird als das erste " auf "?" entfernt (das
>"? matcht auf den Nullstring, das zweite " aus der Regex matcht auf das
>erste " in "?").

Also im Klar-Text: Sucht null oder ein /"/ gefolgt von einem weiteren
/"/, korrekt? Das trifft doch aber dann nur auf einen String /"/ oder
/""/ zu - nicht aber auf /"?"/ wie in der Peter-Pan-Zeile. Oder übersehe
ich da schon wieder was? Was meinst du denn mit *Nullstring*?

>Weil diese zweite Regex (die aus der Substitution) gematcht hat, werden
>die Variablen $1, $2 aus dem ersten Match überschrieben. Weil die zweite
>Regex keine Klammern enthält, sind $1 und $2 nun undefiniert.

Yepp, I see.

>Du willst vermutlich einfach sowas wie:

>oder
>
> if ($test[2] =~ m/^"(\d?\?)"$/) {
> $KK = $1;
> $test[2] ="";
> ...

Ja, wobei in dem obigen RegExp dieselbe Falle noch mal 'von hinten'
zuschlägt, weil in der Variable /?"/ steckt und das hintere /"/ mit in
der Variable landet. ;o) So geht's aber:

=~ m/^(")(\d?\?)(")$/ # das zwischen den " in $2

>
>Wenn du unbedingt deinen alten Ansatz behalten willst, musst du die
>Match-Variablen $1 und $2 vor einem neuen Matching abspeichern und
>quotemeta() bzw. \Q benutzen:

Hab' ich probiert - tut auch.

Bleibt noch das Problem mit den *Phantom-Semikola*.

Warum funktioniert in der Pan- und Pirelli-Zeile, also da, wo das letzte
Feld leer ist, dies nicht:

else {test[3] = "KK-04:" . $KK;
$test[3] =~ s/^(.+)$/"$1"/;}

Die erste Anweisung schreibt doch /?/ bzw. /1?/ in das Feld und die
müssten doch von der Suchanweisung gematcht und durch "" ersetzt
werden?! Stattdessen kommen 15 (sic!) Semikola.

Kann man denn das Einfassen mit /"/ nicht direkt in der ersten Anweisung
erledigen? Wenn ja: wie?

TIA,

--
Thomas Pothmann
PGP-Key on demand

Re: RegExp matcht nur 2. Alternative - ratlos

am 10.03.2006 09:45:52 von Christian Winter

Thomas Pothmann schrieb:
[...]
> else {test[3] = "KK-04:" . $KK;
> $test[3] =~ s/^(.+)$/"$1"/;}
>
[...]
>
> Kann man denn das Einfassen mit /"/ nicht direkt in der ersten Anweisung
> erledigen? Wenn ja: wie?

Du meinst etwas wie folgendes?
$test[3] = '"KK-04:' . $KK . '"';

-Christian

Re: RegExp matcht nur 2. Alternative - ratlos

am 10.03.2006 10:41:06 von Frank Seitz

Christian Winter wrote:
> Thomas Pothmann schrieb:
>>
>>else {test[3] = "KK-04:" . $KK;
>> $test[3] =~ s/^(.+)$/"$1"/;}
>
>>Kann man denn das Einfassen mit /"/ nicht direkt in der ersten Anweisung
>>erledigen? Wenn ja: wie?
>
> Du meinst etwas wie folgendes?
> $test[3] = '"KK-04:' . $KK . '"';

$test[3] = qq("KK-04:$KK");

Grüße
Frank
--
Dipl.-Inform. Frank Seitz; http://www.fseitz.de/
Anwendungen für Ihr Internet und Intranet
Tel: 04103/180301; Fax: -02; Industriestr. 31, 22880 Wedel

Re: RegExp matcht nur 2. Alternative - ratlos

am 10.03.2006 18:26:50 von t_pot

Christian Winter wrote:

>Thomas Pothmann schrieb:

>> Kann man denn das Einfassen mit /"/ nicht direkt in der ersten Anweisung
>> erledigen? Wenn ja: wie?

>Du meinst etwas wie folgendes?
> $test[3] = '"KK-04:' . $KK . '"';

Ja genau! Das tut. Die Variante von Frank auch. Thanks.

Noch mal zu dem Vorschlag mit dem CSV-Modul. Ich hab' mich mal bissl auf
CPAN umgesehen und bin erschlagen von dem Haufen, den es zu diesem Thema
gibt.

Kann mir wer was empfehlen? Das Teil muss hier ja nicht viel können;
also zum Bleifisch /nicht/ aus einer SQL-Datenbank lesen. Im Grunde
reicht, daß es die Feldtrenner in der CSV-Datei detektieren kann.

Vorschläge?

TIA,

--
Thomas Pothmann
PGP-Key on demand

Re: RegExp matcht nur 2. Alternative - ratlos

am 10.03.2006 19:29:35 von Slaven Rezic

t_pot@lycos.de (Thomas Pothmann) writes:

> Christian Winter wrote:
>
> >Thomas Pothmann schrieb:
>
> >> Kann man denn das Einfassen mit /"/ nicht direkt in der ersten Anweisung
> >> erledigen? Wenn ja: wie?
>
> >Du meinst etwas wie folgendes?
> > $test[3] = '"KK-04:' . $KK . '"';
>
> Ja genau! Das tut. Die Variante von Frank auch. Thanks.
>
> Noch mal zu dem Vorschlag mit dem CSV-Modul. Ich hab' mich mal bissl auf
> CPAN umgesehen und bin erschlagen von dem Haufen, den es zu diesem Thema
> gibt.
>
> Kann mir wer was empfehlen? Das Teil muss hier ja nicht viel können;
> also zum Bleifisch /nicht/ aus einer SQL-Datenbank lesen. Im Grunde
> reicht, daß es die Feldtrenner in der CSV-Datei detektieren kann.
>
> Vorschläge?
>

Du brauchst nur Text::CSV_XS oder, falls du nicht kompilieren kannst,
Text::CSV

Gruß,
Slaven

--
Slaven Rezic - slaven rezic de

tknotes - A knotes clone, written in Perl/Tk.
http://ptktools.sourceforge.net/#tknotes

Re: RegExp matcht nur 2. Alternative - ratlos

am 12.03.2006 14:16:16 von t_pot

Slaven Rezic wrote:

>t_pot@lycos.de (Thomas Pothmann) writes:

>> Noch mal zu dem Vorschlag mit dem CSV-Modul. Ich hab' mich mal bissl auf
>> CPAN umgesehen und bin erschlagen von dem Haufen, den es zu diesem Thema
>> gibt.

>Du brauchst nur Text::CSV_XS oder, falls du nicht kompilieren kannst,
>Text::CSV

Danköö! Hab mir Text::CSV gesaugt und werde mir das bei Gelegenheit mal
anschauen.

So far,

--
Thomas Pothmann
PGP-Key on demand