Schrift
[thread]7839[/thread]

backreferences und '\n' (Seite 2)



<< |< 1 2 >| >> 15 Einträge, 2 Seiten
snarf
 2006-03-30 11:43
#64176 #64176
User since
2003-08-14
77 Artikel
BenutzerIn
[default_avatar]
Okay, dann möchte ich eben noch mal kurz zur generellen Auflösung beitragen, damit hier nicht weiter unnötig diskutiert werden muß :)

Da ganze war ein 2 Minuten-"Hack" der typischen Art: Kurz vor dem Mittagessen, ein Kollege ruft an. Ob ich nicht mal eben eine Datei für ihn ändern könnte. Es geht um einzelne Zeilen innerhalb der Datei, mit Aussehen, wie folgt " titel = { xyz }, ".
Einzige Hinweise: Die Zeile kann sich über mehrere Zeilen erstrecken (also '\n' included), Steuerzeichen etc. weiß er nicht, nur die äußeren {} sollen gedoppelt werden (innere {} seien möglich) ... definitives Ende des Eintrages, sobald die nächste Zeile mit " dummy = {" beginnt.

Mittlerweile weiß ich, daß es sich um eine LaTex-Datei gehandelt hat.

Gut, also mal eben neben sonstiger Dinge, die deutlich wichtiger waren und ohne die Datei vorliegend zu haben, den Code entworfen und zur Verfügung gestellt (obligatorisches Statement: "Wenn noch was zu ändern ist, dann melde Dich"). Natürlich an dieser Stelle auch die mehrzeiligen Einträge zu einzeiligen zusammengefaßt.

Kurze Zeit später die Rückmeldung: "Funktioniert bei einzeiligen Einträgen, nicht bei mehrzeiligen!".

Gut, natürlich hätte bereits an dieser Stelle ein simples

Code: (dl )
1
2
$text =~ s/{/{{/;
$text =~ s/}/}}/;


funktioniert, aber ich war ersteinmal viel zu verwundert, daß folgendes Konsrukt nicht auf anhieb funktionierte:

Code: (dl )
$title =~ s /(\{.+\}/\{$1\}/


Leider in meiner Verwunderung (und meinem aufkeimenden Hungergefühl) nicht über perldoc perlop nachgedacht und hier meine Frage gepostet ...

Soweit, so gut. Dank Taulmarill und Vayu das ganze schnell noch mit '/s' gefixt und schon war's erledigt.

Quote
Die bedeutung Single/Multi line ist auch nicht wirklich passend.


Darf ich dann fragen, was Du hier für passender hälst???

Gruß
Dirk\n\n

<!--EDIT|snarf|1143705553-->
sid burn
 2006-03-30 19:50
#64177 #64177
User since
2006-03-29
1520 Artikel
BenutzerIn

user image
[quote=snarf,30.Mar..2006, 09:43]
Quote
Die bedeutung Single/Multi line ist auch nicht wirklich passend.


Darf ich dann fragen, was Du hier für passender hälst???

Gruß
Dirk[/quote]
Eine kurze passendere Beschreibung wüste ich auf anhieb auch nicht. Allerdings suggeriert single/multi line Modus doch, dass die Variable auf der wir die regex anwenden anders behandelt wird. Dies ist aber nicht der Fall. Genauso kann man denken das eine gleichzeitige Verwendung von single/multi line Modus nicht Funktioniert, oder es keinen Sinn ergibt. Aber man kann schon beide Optionen zusammen verwenden.

Das kann man in "perldoc perlre" nachschlagen.

Ich würde die Optionen jedenfalls so umbennen, auf das was sie wirklich machen. Also für /s "Verändert die Bedeutung des Punktes" und für /m "Verändert die Bedeutung von Zirkumflex und Dollarzeichen.

Die Sache ist halt nur die, dass diese Veränderung letztendlich schon dazu führen das eine Veriable als Single Line oder Multi Line anerkannt wird. Allerdings nur, wenn man halt diese Zeichen benutzt.

Auch denken viele das wenn sie eine Variable benutzen die mehrere Zeilen enthält, dass Sie automatisch /m oder /s benutzen müssen. Halt wegem dem Multi Line Modus. Wenn Sie aber "^" , "$" oder "." nicht in ihrer Regex verwenden braucht man diese Option nicht. Die Option bewirkt letztendlich nichts.


Zum anderen liest man in vielen Einführungen von Regulären Ausdrücken immer eine Falsche Bedeutung des Punktes. Meistens liest man das der Punkt auf jedes Zeichen passt. Was aber falsch ist. Der Punkt passt auf jedes Zeichen das nicht ein Newline Zeichen ist. Es ist also Vergleichbar mit [^\n].

Wenn man diese Tatsache weiß, dann ist es letztendlich auch nicht verwunderlich warum deine erste Regex nicht funktionierte. Den das ".+" frisst erst alle Zeichen bis zum Newline Zeichen auf, und wird dann mittels Backtracking gezwungen auf das "}" zu matchen. Wenn aber halt auf der selben Zeile kein "}" vorkommt, dann passt deine Regex nicht, und macht letztendlich mit dem nächsten "{" Zeichen weiter. Da du aber mit dem Punkt nie über eine Zeile hinweg kommst, machst du deine Suche nicht über mehrere logische Zeilen.

Du hast entweder die wahl die Bedeutung des Punktes zu verändert, oder eben einen Ausdruck zu benutzen der auch auf "\n" passt, aber eben nicht auf das Zeichen, was du als nächstes benötigst. Das wäre wie Taulmarill schrieb am besten " [^}] " was auf jedes Zeichen passt auser dem "}". Es passt also letztendlich auch auf das Newline Zeichen.

Die Option /s oder /m benötigen wir nicht. Da wir nirgendswo ein Punkt, Zirkumflex oder ein Dollar Zeichen benutzen, obwohl unsere variable aus mehreren Zeilen besteht.

Von daher würde ich schon sagen das /s oder /m auf den ersten Blick, oder für jemanden der sich nicht so viel mit Regulären Ausdrücken beschäftigt keine richtige und eindeutige Bezeichnung ist.

Ich hoffe meine Erklärung ist nachzuvollziehen.

---------

Zum anderen würde ich dir auch raten lieber Taulmarills Lösung zu benutzen. Dein Fehler in der Regex ist folgender das erst nach einem "{" gesucht wird. Danach wird mit ".+" die komplette restliche Datei eingelesen, bis zum EOF. Danach wird mittels BackTracking solange zurück gegangen wie ein "}" gefunden wird.

Wenn du das auf eine 100MB Datei anwendest, und am anfang und am ende jeweils ein "{" oder "}" steht, dann müsste letztendlich die komplette 100MB nochmal zusätzlich in $1 gespeichert werden. Und dann findet erst eine ersetzung statt. Existiert keine richtige Anzahl von öffnenden und schließenden Klammern in deiner Datei, Funktioniert deine Regex auch nicht richtig, so wie meine Lösung.

Es kommt dann bei solch einem String.
"print { "hallo, welt" } print; }"
folgendes bei deiner Regex heraus.
"print {{ "hallo, welt" } print; }}"

Besser wäre es wenn du zumindest einen nicht gierigen Operator, also ".+?" benutzt. Dann würde das gleiche Ergebnis heraus kommen wie Taulmarills Lösung. Er würde auch nicht sofort alles bis zum EOF einlesen, wäre also Speicherschonender und schneller.

Allerdings ist Taulmarills Lösung trotzdem noch effizenter, da durch einen nicht gierigen Operator eine Menge Backtracking Operationen ausgeführt werden.

Wenn ich aber jetzt schon über effizenz rede, dann könnte man Taulmarills Lösung noch effizenter machen, wenn man Atomare Klammern hinzufügen würde.

Code: (dl )
1
2
3
4
5
6
7
8
s/(
{
(?>
[^}]+
)
}
)
/{$1}/gx;

Dadurch wird ein Ergebnis im Fehlerfall (keine schließende Klammer) schneller gefunden.

Ich schreibe schon wieder zu viel...\n\n

<!--EDIT|sid burn|1143736266-->
Nicht mehr aktiv. Bei Kontakt: ICQ: 404181669 E-Mail: perl@david-raab.de
snarf
 2006-03-31 11:02
#64178 #64178
User since
2003-08-14
77 Artikel
BenutzerIn
[default_avatar]
[quote=sid burn,30.03.2006, 17:50]
Ich schreibe schon wieder zu viel...
[/quote]

... :)

Nein, nicht wirklich. Ich gehöre ebenfalls zu der Fraktion der "lieber einmal zu ausführlich als zu kurz" - Erklärer, von daher sind Deine Ausführungen für alle Fremdleser sicherlich herzlichst willkommen! Und in diesem Falle natürlich auch für mich zur "Auffrischung" ... :)

Ich lasse die Behandlungen der Effizienz einfach mal im Raume stehen (ohne sie anzuzweifeln) und ebenso die theoretische "Fehleranfälligkeit" - ich brauchte mir in beiden Fällen darum keine Gedanken zu machen.

Die Datei (knappe 500 kb) wurde zeilenweise eingelesen und die jeweils eingelesene Zeile sofort untersucht, bei Vorkommen des geforderten Terms die entsprechenden Anpassungen vorgenommen (max 1 KB pro Term, ein '.+' mit '/s' tat hier also ausreichend gute Dienste) und dann sofort wieder in die Kopie geschreiben. Da es sich, wie schon gesagt, um eine originäre und "fehlerfreie" LaTeX Datei handelte, waren die einschließenden '{}' in jedem Falle gegeben.

Tja ... und da "verteidige" ich fleißig die von mir gewählte Vorgehensweise und merke dennoch schon, daß sich Taulmarills (und Deine) Regex bei mir im Kopf festgesetzt hat - und ich wahrscheinlich zu dieser greifen würde, wenn ich noch einmal vor dem Problem stehen würde :) :)

funny ...

Have fun!
Dirk
sid burn
 2006-03-31 21:09
#64179 #64179
User since
2006-03-29
1520 Artikel
BenutzerIn

user image
[quote=snarf,31.Mar..2006, 09:02]Tja ... und da "verteidige" ich fleißig die von mir gewählte Vorgehensweise und merke dennoch schon, daß sich Taulmarills (und Deine) Regex bei mir im Kopf festgesetzt hat ...[/quote]
Mal eine andere Frage, eigentlich kann deine erste Regex doch gar nicht richtig Funktioniert haben?

Code: (dl )
s/ ( { .+ } ) /{$1}}/gsx;


Wenn du diese Regex anwendest, dann wird nur das allerste { und das allerletzte } verdoppelt. Alles was dazwischen ist wird nicht beachtet.

Was eigentlich auch logisch ist, das ".+" frist alles bis zum EOF auf, geht zurück zum letzten "}" dort matcht die regex, es findet eine Ersetzung statt. Danach geht die Regex NACH dem gematchten text weiter. Also alle öffnenden und schließenden Klammern zwischen den beiden Zeichen werden nicht beachtet.

Eine while Schleife kannst du aber auch nicht auf die Variable angewendet haben, weil das dann eine Endlosschleife wäre. Wie hast du es genau hinbekommen das deine Regex das gewünschte macht? ^^

Wenn du keinen Nicht Gierigen Quantor genommen hast, oder die ganze Datei in einer variable drin stand, dann kann das gar nicht Funktioniert haben.

Hier mal folgendes Programm mit deiner Regex was ich auf das Programm selber anwende.

Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
$ ./klam.pl klam.pl
#!/usr/bin/perl -w

while (<>)
{{
$text .= $_;
}
$_ = $text;

s/ ( { .+ } )/{$1}}/gsx;
print;

Wie man sieht wird nur das erste und letzte zeichen verdoppelt.



Quote
Nein, nicht wirklich. Ich gehöre ebenfalls zu der Fraktion der "lieber einmal zu ausführlich als zu kurz" - Erklärer, von daher sind Deine Ausführungen für alle Fremdleser sicherlich herzlichst willkommen! Und in diesem Falle natürlich auch für mich zur "Auffrischung" ...

Ja, ich erkläre und Begründe immer gerne meine Antworten, und dass auch ausführlich. Wird aber nicht immer gut angesehen, meistens auch mehr als "klugscheißerei". Was ich aber so natürlich nicht meine Absicht ist.\n\n

<!--EDIT|sid burn|1143825917-->
Nicht mehr aktiv. Bei Kontakt: ICQ: 404181669 E-Mail: perl@david-raab.de
snarf
 2006-04-03 11:18
#64180 #64180
User since
2003-08-14
77 Artikel
BenutzerIn
[default_avatar]
[quote=sid burn,31.03.2006, 19:09][quote=snarf,31.Mar..2006, 09:02]Tja ... und da "verteidige" ich fleißig die von mir gewählte Vorgehensweise und merke dennoch schon, daß sich Taulmarills (und Deine) Regex bei mir im Kopf festgesetzt hat ...[/quote]
Mal eine andere Frage, eigentlich kann deine erste Regex doch gar nicht richtig Funktioniert haben?

Code: (dl )
s/ ( { .+ } ) /{$1}}/gsx;


Wenn du diese Regex anwendest, dann wird nur das allerste { und das allerletzte } verdoppelt. Alles was dazwischen ist wird nicht beachtet.

Was eigentlich auch logisch ist, das ".+" frist alles bis zum EOF auf, geht zurück zum letzten "}" dort matcht die regex, es findet eine Ersetzung statt. Danach geht die Regex NACH dem gematchten text weiter. Also alle öffnenden und schließenden Klammern zwischen den beiden Zeichen werden nicht beachtet.[/quote]
... es war ja genau mein Wunsch, lediglich die umschließenden {} zu doppeln - und nicht die {}, die sich auch noch innerhalb befinden.

Und da sich mehrere zusammengehörige Datei-Zeilen innerhalb eines Skalars befanden, konnte ich auch mittels '/s' und einem greedy operator alles doppeln - wie gesagt, ich brauchte mir um Fehlerfälle keine Sorgen zu machen.

Habe jetzt mal "den Hintergrund" zu meiner Aufgabe nachgefragt. Die Datei, die ich vorgesetzt bekommen habe, ist wohl eine sogenannte Biblio-Datei aus LaTeX. Sie enthält die Artikel-Referenzen zu einer Arbeit meines Kollegen. Jeder Eintrag dieser Referenzen besteht aus mehreren Tags (ich nenne sie jetzt mal so), die immer die folgende Form besitzt:

Code: (dl )
tagname = { ellenlanger Text, häufig über zig Zeilen };


Durch das Doppeln der umschließenden Zeilen wird wohl verhindert, das LaTeX den gesamten umschließenden Text seiner Groß-/Kleinschreibkorrektur unterzieht (was ja bei Eigennamen etc durchaus sinnvoll ist). Mehr kann ich dazu nicht sagen ...
<< |< 1 2 >| >> 15 Einträge, 2 Seiten



View all threads created 2006-03-29 15:15.