Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Text ersetzen - aber nicht innerhalb von Link-Tags

11 views
Skip to first unread message

Sven Paulus

unread,
Mar 11, 2012, 8:38:39 PM3/11/12
to
Hi zusammen,

ich bin gerade ein wenig am Regex-Rumknobeln: Ich moechte gerne in einem
HTML-Dokument jedes Vorkommnis eines bestimmten Strings ersetzen, aber
nicht, wenn dieser String (irgendwo) innerhalb von einem a-Tag-Paerchen
steht.

Wenn ich z.B. als FOO mit BAR ersetzen wollte, dann soll folgendes
passieren:
FOO -> ersetzen
Hallo Du alter FOO! -> ersetzen
<b>FOO</b> -> ersetzen
<a href="...">FOO</a> -> nicht ersetzen
<a href="..."><b>FOO</b></a> -> nicht ersetzen
<a href="...">12 FOO 34</a> -> nicht ersetzen
<a href="..."><b>12 FOO 34</b></a> -> nicht ersetzen

Eigentlich alles soweit nicht tragisch, nur am letzten o.g. Beispiel
scheitere ich. Wenn ich in das negative look ahead fuer das
Schliessen das a-Tags ein ".*?" mit reinnehme, dann matcht alles
nicht mehr.

Z.Z. stehe ich hier:

| #! /usr/bin/perl
|
| $a = '<a href="http://www.example.org">FOO</a> FOO string oder anderer FOO <b>FOO</b> <a href="http://www.example.org"><b>FOO</b></a> <a href="http://www.example.org">Es ist FOO oder so</a> <a href="FOO">Anderer Link</a> <a href="http://www.example.org"><b>Aha - FOO string</b></a> FOO';
|
| $a =~ s/(?!<a[^>]+>)\bFOO\b(?!([^<]*|<[^>]+>)<\/a>)/BAR/g;
|
| print $a, "\n";

Das liefert mir

| <a href="http://www.example.org">FOO</a> BAR string oder anderer BAR <b>BAR</b> <a href="http://www.example.org"><b>FOO</b></a> <a href="http://www.example.org">Es ist FOO oder so</a> <a href="FOO">Anderer Link</a> <a href="http://www.example.org"><b>Aha - BAR string</b></a> BAR

Wie man sieht, eigentlich alles ganz ok, nur des vorletzte BAR sollte eben
so nicht sein ...

Hat jemand 'ne Idee, was ich anders machen koennte? Ja, klar, es gibt
fuer sowas auch geschicktere Loesungsmoeglichkeiten als regular expressions,
nur hat mich hier jetzt doch mal die Neugierde gepackt :)

Gruesse,

Sven

Tim Landscheidt

unread,
Mar 11, 2012, 11:18:08 PM3/11/12
to
Sven Paulus <sv...@karlsruhe.org> wrote:

> [...]
> Hat jemand 'ne Idee, was ich anders machen koennte? Ja, klar, es gibt
> fuer sowas auch geschicktere Loesungsmoeglichkeiten als regular expressions,
> nur hat mich hier jetzt doch mal die Neugierde gepackt :)

| $_ = $a;
| while (/(<a .*?<\/a>|FOO)/g)
| {
| substr ($_, pos () - length ($1), length ($1)) = 'BAR' if ($1 eq 'FOO');
| }

Irgendwie schaffe ich es aber nicht, die Zuweisung an $_
durch ein "$a =~" zu ersetzen. Wenn length ('FOO') != length
('BAR') müsste man wohl noch pos () zuweisen.

Tim

Christian Garbs

unread,
Mar 12, 2012, 4:25:33 PM3/12/12
to
Mahlzeit!

Tim Landscheidt <t...@tim-landscheidt.de> wrote:

> | $_ = $a;
> | while (/(<a .*?<\/a>|FOO)/g)
> | {
> | substr ($_, pos () - length ($1), length ($1)) = 'BAR' if ($1 eq 'FOO');
> | }

> Irgendwie schaffe ich es aber nicht, die Zuweisung an $_
> durch ein "$a =~" zu ersetzen. Wenn length ('FOO') != length
> ('BAR') müsste man wohl noch pos () zuweisen.

Schöner Ansatz! Das substr() kannst Du dann mit s///e umgehen:

$a =~ s/(<a .*?<\/a>|FOO)/$1 eq 'FOO' ? 'BAR' : $1/ge;

Klappt auch mit unterschiedlichen Längen. Die Zeile muss ich mir
jetzt merken, die kann ich bestimmt auch noch öfter gebrauchen :-)

Gruß
Christian
--
sub _{print"\n"}_;for(;$s<9;++$s){$_='1E2018201E00001E2018201E00001E2018201'
.'E002020001C2222221400005CA2A2A27C02001C2222221C20003E040202201F2422221C00'
.'242A2A2A12002020001C2222221F20001C2A2A2A0C';while(s;(..);;){printf'%c',hex
$1&1<<$s?40:32}_}$_=':::Christian Garbs:<mi...@cgarbs.de>',y;:;\t;;print;_;_

Tim Landscheidt

unread,
Mar 12, 2012, 6:27:32 PM3/12/12
to
Christian Garbs <mi...@cgarbs.de> wrote:

>> | $_ = $a;
>> | while (/(<a .*?<\/a>|FOO)/g)
>> | {
>> | substr ($_, pos () - length ($1), length ($1)) = 'BAR' if ($1 eq 'FOO');
>> | }

>> Irgendwie schaffe ich es aber nicht, die Zuweisung an $_
>> durch ein "$a =~" zu ersetzen. Wenn length ('FOO') != length
>> ('BAR') müsste man wohl noch pos () zuweisen.

> Schöner Ansatz! Das substr() kannst Du dann mit s///e umgehen:

> $a =~ s/(<a .*?<\/a>|FOO)/$1 eq 'FOO' ? 'BAR' : $1/ge;

> Klappt auch mit unterschiedlichen Längen. Die Zeile muss ich mir
> jetzt merken, die kann ich bestimmt auch noch öfter gebrauchen :-)

Ich hatte das s///e in die while-Schleife gepackt, und ir-
gendetwas hatte da gehakt. Deine Methode, die ich natürlich
sonst auch immer nutze, habe ich dank Brett vor dem Kopf
dann gar nicht wahrgenommen :-).

Tim

Sven Paulus

unread,
Mar 13, 2012, 12:58:39 PM3/13/12
to
Christian Garbs <mi...@cgarbs.de> wrote:
> Tim Landscheidt <t...@tim-landscheidt.de> wrote:
>> | $_ = $a;
>> | while (/(<a .*?<\/a>|FOO)/g)
>> | {
>> | substr ($_, pos () - length ($1), length ($1)) = 'BAR' if ($1 eq 'FOO');
>> | }
> Schöner Ansatz! Das substr() kannst Du dann mit s///e umgehen:
> $a =~ s/(<a .*?<\/a>|FOO)/$1 eq 'FOO' ? 'BAR' : $1/ge;

Wow, danke Euch beiden, sehr huebsche Loesung. Irgendwie war ich viel zu
arg auf (?!..) festgemauert, um noch mal eigentlich eine so logische
und klare Idee zu kommen. Klasse - und das beste: sie funktioniert! :)

Gruesse,

Sven
Message has been deleted

Tim Landscheidt

unread,
Mar 13, 2012, 10:54:31 PM3/13/12
to
Martin Τrautmann <t-us...@gmx.net> wrote:

>> Wow, danke Euch beiden, sehr huebsche Loesung. Irgendwie war ich viel zu
>> arg auf (?!..) festgemauert, um noch mal eigentlich eine so logische
>> und klare Idee zu kommen. Klasse - und das beste: sie funktioniert! :)

> Glückwunsch - ich würde da mit brutaler Gewalt vorgehen, erst alles im
> URL gegen einen Platzhalter vertauschen, dann alles andere, dann den
> Platzhalter zurücktauschen.

> Möglicherweise wäre das sogar performanter?

Wieso sollte es das sein? Da müsste der Text dann ja mehrfach
durchgekaut werden. Christians Lösung bewegt sich einmal von
Textanfang zu Textende. Aber gemessen habe ich das natürlich
jetzt nicht :-).

Tim

Tina Müller

unread,
Mar 15, 2012, 8:39:47 AM3/15/12
to
Sven Paulus wrote:
> ich bin gerade ein wenig am Regex-Rumknobeln: Ich moechte gerne in einem
> HTML-Dokument jedes Vorkommnis eines bestimmten Strings ersetzen,

ich lese "Regex" und "HTML" und denke: falsch.
in den meisten fällen ist es einfacher, einen html-parser
zu benutzen (HTML::Parser, HTML::TreeBuilder, ...)

das lohnt sich dann auch, falls sich das HTML mal leicht
verändert und eine regex nicht mehr matcht.

das nur als anregung.

--
http://www.perl-community.de/
http://perlpunks.de/

Thomas 'PointedEars' Lahn

unread,
Mar 25, 2012, 5:59:11 AM3/25/12
to
Tina Müller wrote:

> Sven Paulus wrote:
>> ich bin gerade ein wenig am Regex-Rumknobeln: Ich moechte gerne in einem
>> HTML-Dokument jedes Vorkommnis eines bestimmten Strings ersetzen,
>
> ich lese "Regex" und "HTML" und denke: falsch.
> in den meisten fällen ist es einfacher, einen html-parser
> zu benutzen (HTML::Parser, HTML::TreeBuilder, ...)
>
> das lohnt sich dann auch, falls sich das HTML mal leicht
> verändert und eine regex nicht mehr matcht.
>
> das nur als anregung.

Du nimmst mir die Worte aus dem Tastertur. HTML gehört zur Klasse der
kontextfreien (Chomsky L2), nicht-regulären Sprachen, die nur mit einem
nichtdeterministischen Kellerautomaten (vulgo: Parser) verarbeitet werden
können.

[Beweis (nicht hinreichend): `<a>b</a>' lässt sich als der Klammerausdruck
`()' schreiben, wenn man `<a>' durch `(', `b' durch `' und `</a>' durch
`)' ersetzt. Oder es lässt sich als `()()' schreiben, wenn man `<' durch
`(', `a', `b' und `/' durch `' und `>' durch `)' ersetzt. Mithin ist
HTML – vereinfacht – eine Variante der Sprache korrekter
Klammerausdrücke. (Die Anwendung der entsprechenden Pumping-Lemmata
bleibt dem geneigten Leser zur Übung überlassen.)]

Hingegen beschreiben reguläre Ausdrücke die regulären Sprachen (L3), die mit
einem endlichen Automaten verarbeitet werden können.

Jede reguläre Sprache ist auch kontextfrei, aber nicht jede kontextfreie
Sprache ist auch regulär. Daher eignen sich reguläre Ausdrücke nur bedingt
zur Beschreibung kontextfreier Sprachen. *Ein* regulärer Ausdruck *allein*
genügt dafür sicher nicht.

Ausserdem sollte sich der OP genauer mit SGML/HTML und dessen Terminologie
auseinandersetzen, die er derzeit (wie leider so viele) falsch verwendet.
Er meint in Wirklichkeit nicht "Link-Tags" und er meint auch nicht
"innerhalb von Link-Tags", sondern "innerhalb von _A-Elementen_":

<http://www.w3.org/TR/REC-html40/intro/sgmltut.html#h-3.2.1>

--
PointedEars

Please do not Cc: me. / Bitte keine Kopien per E-Mail.

Thomas 'PointedEars' Lahn

unread,
Mar 25, 2012, 6:15:15 AM3/25/12
to
Christian Garbs wrote:

> Tim Landscheidt <t...@tim-landscheidt.de> wrote:
>> | $_ = $a;
>> | while (/(<a .*?<\/a>|FOO)/g)
>> | {
>> | substr ($_, pos () - length ($1), length ($1)) = 'BAR' if ($1 eq
>> | 'FOO');
>> | }
>
>> Irgendwie schaffe ich es aber nicht, die Zuweisung an $_
>> durch ein "$a =~" zu ersetzen. Wenn length ('FOO') != length
>> ('BAR') müsste man wohl noch pos () zuweisen.
>
> Schöner Ansatz! Das substr() kannst Du dann mit s///e umgehen:
>
> $a =~ s/(<a .*?<\/a>|FOO)/$1 eq 'FOO' ? 'BAR' : $1/ge;
>
> Klappt auch mit unterschiedlichen Längen. Die Zeile muss ich mir
> jetzt merken, die kann ich bestimmt auch noch öfter gebrauchen :-)

Zweifelhaft, siehe andere Antwort. Abgesehen von ungültigem Markup: Dieser
Ansatz funktioniert gemäss aktuellem Working Draft schon mit HTML5 nicht
mehr, mit dem

<a href="" title="</a>">b</a>

syntaktisch korrekt ist [1] und somit folgenden Parse-Unterbaum erzeugt:

element(a)
attribute(href)
text("")
attribute(title)
text("</a>")
text("b")

(Interessanterweise hat der W3C-Validator daran auch als HTML 4.01 nichts
auszusetzen.)

Mit obigem Ansatz würde jedoch

<a .*?</a>

auf

<a href="" title="</a>

matchen.

______
[1] <http://www.w3.org/TR/html5/tokenization.html#attribute-value-double-
quoted-state>

Peter J. Holzer

unread,
Mar 25, 2012, 6:27:11 AM3/25/12
to
On 2012-03-25 09:59, Thomas 'PointedEars' Lahn <Point...@web.de> wrote:
> Tina Müller wrote:
>> Sven Paulus wrote:
>>> ich bin gerade ein wenig am Regex-Rumknobeln: Ich moechte gerne in einem
>>> HTML-Dokument jedes Vorkommnis eines bestimmten Strings ersetzen,
>>
>> ich lese "Regex" und "HTML" und denke: falsch.
>> in den meisten fällen ist es einfacher, einen html-parser
>> zu benutzen (HTML::Parser, HTML::TreeBuilder, ...)
>>
>> das lohnt sich dann auch, falls sich das HTML mal leicht
>> verändert und eine regex nicht mehr matcht.
>>
>> das nur als anregung.
>
> Du nimmst mir die Worte aus dem Tastertur. HTML gehört zur Klasse der
> kontextfreien (Chomsky L2), nicht-regulären Sprachen, die nur mit einem
> nichtdeterministischen Kellerautomaten (vulgo: Parser) verarbeitet werden
> können.
>
> [Beweis (nicht hinreichend): `<a>b</a>' lässt sich als der Klammerausdruck
> `()' schreiben, wenn man `<a>' durch `(', `b' durch `' und `</a>' durch
> `)' ersetzt. Oder es lässt sich als `()()' schreiben, wenn man `<' durch
> `(', `a', `b' und `/' durch `' und `>' durch `)' ersetzt. Mithin ist
> HTML – vereinfacht – eine Variante der Sprache korrekter
> Klammerausdrücke. (Die Anwendung der entsprechenden Pumping-Lemmata
> bleibt dem geneigten Leser zur Übung überlassen.)]
>
> Hingegen beschreiben reguläre Ausdrücke die regulären Sprachen (L3), die mit
> einem endlichen Automaten verarbeitet werden können.

Allerdings sind Perl-Regexps keine regulären Ausdrücke im formalen Sinn,
sondern eine Erweiterung davon. Insbesondere kann man damit Klammern
matchen (Beispiele siehe FAQ seit ewigen Zeiten).

Die von diversen Leuten gebetsmühlenartig vorgetragene Begründung, dass
man mit regulären Ausdrücken keine kontextfreien Sprachen parsen könne,
ist daher eine Themenverfehlung. (Mal abgesehen davon, dass für viele
Aufgabenstellungen (darunter das hier diskutierte "jedes Vorkommnis
eines bestimmten Strings ersetzen") ein HTML-File als reguläre Sprache
betrachtet werden kann, weil die Verschachtelung der Elemente irrelevant
ist.)

Richtig ist allerdings, dass regexp-basierte Ansätze meistens fragil
und/oder unwartbar sind. Für ernsthaft eingesetzte Programme ist daher
auf jeden Fall ein Parser vorzuziehen.

Allerdings hat Sven bereits im Ausgangsposting klargestellt, dass es ihm
nur um die intellektuelle Herausforderung geht und nicht darum, eine
praxistaugliche Lösung zu bekommen.

hp


--
_ | Peter J. Holzer | Deprecating human carelessness and
|_|_) | Sysadmin WSR | ignorance has no successful track record.
| | | h...@hjp.at |
__/ | http://www.hjp.at/ | -- Bill Code on as...@irtf.org

Thomas 'PointedEars' Lahn

unread,
Mar 25, 2012, 6:18:53 PM3/25/12
to
Peter J. Holzer wrote:
Das ist nur teilweise richtig. Mit der rekursiven Erweiterung in Perl-
Regexps bzw. PCREs muss für jede zusätzliche Rekursion Speicher reserviert
werden. Klammersprachen können daher so nur eingeschränkt geparst werden.
man perlre.

> Die von diversen Leuten gebetsmühlenartig vorgetragene Begründung, dass
> man mit regulären Ausdrücken keine kontextfreien Sprachen parsen könne,
> ist daher eine Themenverfehlung.

Ex falso quodlibet. Und ich jedenfalls habe das hier *so* _nicht_
behauptet.

> (Mal abgesehen davon, dass für viele Aufgabenstellungen (darunter das hier
> diskutierte "jedes Vorkommnis eines bestimmten Strings ersetzen") ein
> HTML-File als reguläre Sprache betrachtet werden kann, weil die
> Verschachtelung der Elemente irrelevant ist.)

Das ist grober Unfug.

> Richtig ist allerdings, dass regexp-basierte Ansätze meistens fragil
> und/oder unwartbar sind. Für ernsthaft eingesetzte Programme ist daher
> auf jeden Fall ein Parser vorzuziehen.
>
> Allerdings hat Sven bereits im Ausgangsposting klargestellt, dass es ihm
> nur um die intellektuelle Herausforderung geht und nicht darum, eine
> praxistaugliche Lösung zu bekommen.

Wie kommst Du auf dies schmale Brett? Die intellektuelle Herausforderung
ist es hier, eine praxistaugliche Lösung zu erhalten. Darum geht es auch
Sven. Dafür braucht man nicht unbedingt eine Parser-Klasse, aber *eine*
RegExp reicht dafür nun einmal auch nicht aus (insbesondere nicht die
vorgeschlagene, wie bereits gezeigt).

Peter J. Holzer

unread,
Mar 26, 2012, 6:14:41 PM3/26/12
to
On 2012-03-25 22:18, Thomas 'PointedEars' Lahn <Point...@web.de> wrote:
> Peter J. Holzer wrote:
>> Thomas 'PointedEars' Lahn wrote:
[kleiner Ausflug in die Welt der formalen Sprachen]
>>
>> Allerdings sind Perl-Regexps keine regulären Ausdrücke im formalen Sinn,
>> sondern eine Erweiterung davon. Insbesondere kann man damit Klammern
>> matchen (Beispiele siehe FAQ seit ewigen Zeiten).
>
> Das ist nur teilweise richtig. Mit der rekursiven Erweiterung in Perl-
> Regexps bzw. PCREs muss für jede zusätzliche Rekursion Speicher reserviert
> werden. Klammersprachen können daher so nur eingeschränkt geparst werden.
> man perlre.
>
>> Die von diversen Leuten gebetsmühlenartig vorgetragene Begründung, dass
>> man mit regulären Ausdrücken keine kontextfreien Sprachen parsen könne,
>> ist daher eine Themenverfehlung.
>
> Ex falso quodlibet. Und ich jedenfalls habe das hier *so* _nicht_
> behauptet.

Um so schlimmer. Es wäre nämlich richtig. Nur für Perl irrelevant.

Allerdings frage ich mich dann, wozu Dein kleiner Ausflug in die Theorie
gut sein sollte. Bisschen Namedropping zum Einstand?


>> (Mal abgesehen davon, dass für viele Aufgabenstellungen (darunter das hier
>> diskutierte "jedes Vorkommnis eines bestimmten Strings ersetzen") ein
>> HTML-File als reguläre Sprache betrachtet werden kann, weil die
>> Verschachtelung der Elemente irrelevant ist.)
>
> Das ist grober Unfug.

Ach, Du ungläubiger Thomas!


#!/usr/bin/perl
use warnings;
use strict;

# schlürf
my $html = do { local $/ = undef; <> };

# tags ...
my $param = qr{ [-a-z]+ = " [^"]* " }x;
my $start_tag = qr{ < [a-z]+ (?: \s+ $param )* \s* /? > }x;
my $end_tag = qr{ </ [a-z]+ > }x;
my $comment = qr{ <!-- .*? --> }sx;

# normaler Text ...
my $pcdata = qr{ [^<]*? }x;

# Nested links in HTML sind verboten. Also beginnen wir mit einem
# Starttag <a ...> und endem beim ersten Endtag </a>. Ansonsten
# sind beliebige Start- und Endtags, Kommentare und Text erlaubt.
# Wir wollen darin auch nicht ersetzen, also behandeln wir das als
# Einheit:

my $link = qr{
<a (?: \s+ $param )* \s* >
(?:
$start_tag | $end_tag | $comment | $pcdata
) *?
</a>
}x;

# und jetzt kommen wir zum Eingemachten: Das ganze File ist eine Folge
# von dem ganzen Zeug, was wir oben definiert haben und dem Wort FOO,
# und nur letzteres wollen wir durch BAR ersetzen; Und wenn wir das
# gemacht haben, dann machen wir an der Stelle weiter.

$html =~ s{
\G
(
(?:
(?>
$link | $start_tag | $end_tag | $comment
)*
$pcdata
)*
)
FOO
}
{$1BAR}xg;

print $html;
__END__

Da sind einige offensichtliche Auslassungen drin. Sie zu finden und den
Code zu ergänzen, sei dem Studenten als Übungsaufgabe überlassen.

Was da nicht drin ist, ist irgendwas, was einen Kellerautomaten brauchen
würde. Das kann man auch als State-Machine schreiben (und müsste dann
nicht erst das Backtracking von perl austricksen).


>> Richtig ist allerdings, dass regexp-basierte Ansätze meistens fragil
>> und/oder unwartbar sind. Für ernsthaft eingesetzte Programme ist daher
>> auf jeden Fall ein Parser vorzuziehen.
>>
>> Allerdings hat Sven bereits im Ausgangsposting klargestellt, dass es ihm
>> nur um die intellektuelle Herausforderung geht und nicht darum, eine
>> praxistaugliche Lösung zu bekommen.
>
> Wie kommst Du auf dies schmale Brett?

Zitat:

| Ja, klar, es gibt fuer sowas auch geschicktere Loesungsmoeglichkeiten
| als regular expressions, nur hat mich hier jetzt doch mal die Neugierde
| gepackt :)

Das klingt für mich nicht so, als ob er eine "geschickte" Lösung sucht,
sondern er will wissen, ob das mit Regexp überhaupt geht, und wenn ja,
wie.


> Die intellektuelle Herausforderung ist es hier, eine praxistaugliche
> Lösung zu erhalten. Darum geht es auch Sven.

Deine Allwissenheit ist immer wieder bewunderswert. Was täten wir ohne
Dich! In den Newsgroups wäre nur Irrsal und Wirrsal.

> Dafür braucht man nicht unbedingt eine Parser-Klasse, aber *eine*
> RegExp reicht dafür nun einmal auch nicht aus

Doch, eine Regexp reicht, siehe oben. Als Praxistauglich würde ich das
allerdings nicht bezeichnen, obwohl es schnell geschrieben war (wenn man
davon absieht, dass ich mit (?>...) noch nicht so vertraut bin (gibts ja
erst seit 4 Jahren und ich bin ein alter Knacker und schon ein bisschen
verkalkt) und daher die richtige Stelle nicht auf Anhieb gefunden habe)
und eigentlich auch nicht schwer lesbar ist. Nur wenn man nicht-triviale
Änderungen machen will, wird's grindig.

Thomas 'PointedEars' Lahn

unread,
Mar 28, 2012, 2:06:23 PM3/28/12
to
Peter J. Holzer wrote:

> On 2012-03-25 22:18, Thomas 'PointedEars' Lahn <Point...@web.de> wrote:
>> Peter J. Holzer wrote:
>>> Thomas 'PointedEars' Lahn wrote:
> [kleiner Ausflug in die Welt der formalen Sprachen]
>>>
>>> Allerdings sind Perl-Regexps keine regulären Ausdrücke im formalen Sinn,
>>> sondern eine Erweiterung davon. Insbesondere kann man damit Klammern
>>> matchen (Beispiele siehe FAQ seit ewigen Zeiten).
>>
>> Das ist nur teilweise richtig. Mit der rekursiven Erweiterung in Perl-
>> Regexps bzw. PCREs muss für jede zusätzliche Rekursion Speicher
>> reserviert
>> werden. Klammersprachen können daher so nur eingeschränkt geparst
>> werden. man perlre.
>>
>>> Die von diversen Leuten gebetsmühlenartig vorgetragene Begründung, dass
>>> man mit regulären Ausdrücken keine kontextfreien Sprachen parsen könne,
>>> ist daher eine Themenverfehlung.
>>
>> Ex falso quodlibet. Und ich jedenfalls habe das hier *so* _nicht_
>> behauptet.
>
> Um so schlimmer. Es wäre nämlich richtig.

Nein, wäre es nicht. Man kann mit regulären Ausdrück*en* kontextfreie
Sprachen parsen.

> Nur für Perl irrelevant.

Nein, ist es nicht.

> Allerdings frage ich mich dann, wozu Dein kleiner Ausflug in die Theorie
> gut sein sollte. Bisschen Namedropping zum Einstand?

Sinnentnehmendes Lesen ist offensichtlich nicht Deine Stärke.

>>> Richtig ist allerdings, dass regexp-basierte Ansätze meistens fragil
>>> und/oder unwartbar sind. Für ernsthaft eingesetzte Programme ist daher
>>> auf jeden Fall ein Parser vorzuziehen.
>>>
>>> Allerdings hat Sven bereits im Ausgangsposting klargestellt, dass es ihm
>>> nur um die intellektuelle Herausforderung geht und nicht darum, eine
>>> praxistaugliche Lösung zu bekommen.
>>
>> Wie kommst Du auf dies schmale Brett?
>
> Zitat:
>
> | Ja, klar, es gibt fuer sowas auch geschicktere Loesungsmoeglichkeiten
> | als regular expressions, nur hat mich hier jetzt doch mal die Neugierde
> | gepackt :)
>
> Das klingt für mich nicht so, als ob er eine "geschickte" Lösung sucht,
> sondern er will wissen, ob das mit Regexp überhaupt geht, und wenn ja,
> wie.

Siehe oben.

>> Dafür braucht man nicht unbedingt eine Parser-Klasse, aber *eine*
>> RegExp reicht dafür nun einmal auch nicht aus
>
> Doch, eine Regexp reicht,

Nein, sie reicht eben nicht.

> siehe oben.

Obiges zeigt, dass Du mindestens HTML nicht verstanden hast.

Score adjusted

--
PointedEars

Peter J. Holzer

unread,
Mar 31, 2012, 2:37:28 PM3/31/12
to
On 2012-03-28 18:06, Thomas 'PointedEars' Lahn <Point...@web.de> wrote:
> Peter J. Holzer wrote:
>> On 2012-03-25 22:18, Thomas 'PointedEars' Lahn <Point...@web.de> wrote:
>>> Peter J. Holzer wrote:
>>>> Thomas 'PointedEars' Lahn wrote:
>> [kleiner Ausflug in die Welt der formalen Sprachen]
>>>>
>>>> Allerdings sind Perl-Regexps keine regulären Ausdrücke im formalen Sinn,
>>>> sondern eine Erweiterung davon. Insbesondere kann man damit Klammern
>>>> matchen (Beispiele siehe FAQ seit ewigen Zeiten).
>>>
>>> Das ist nur teilweise richtig. Mit der rekursiven Erweiterung in Perl-
>>> Regexps bzw. PCREs muss für jede zusätzliche Rekursion Speicher
>>> reserviert
>>> werden. Klammersprachen können daher so nur eingeschränkt geparst
>>> werden. man perlre.

Falls Du damit ausdrücken willst, dass man mit endlichem Speicher nur
eine endliche Schachtelungstiefe bearbeiten kann, so ist das
trivialerweise richtig, trifft aber IMHO auf jeden Parser zu, der
beliebige Typ-2-Sprachen parsen können soll.

Falls Du meinst, dass Perl-Regexps ein hartcodiertes Limit hätten, so
kann ich das der Dokumentation nicht entnehmen. Dort steht zwar:

Recursing deeper than 50 times without consuming any input string
will result in a fatal error. The maximum depth is compiled into
perl, so changing it requires a custom build.

Aber beachte die Einschränkung "without consuming any input string". Ich
konnte jedenfalls gerade problemlos den String
(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
matchen - das sind mehr als 50 Ebenen.


>>>> Die von diversen Leuten gebetsmühlenartig vorgetragene Begründung, dass
>>>> man mit regulären Ausdrücken keine kontextfreien Sprachen parsen könne,
>>>> ist daher eine Themenverfehlung.
>>>
>>> Ex falso quodlibet. Und ich jedenfalls habe das hier *so* _nicht_
>>> behauptet.
>>
>> Um so schlimmer. Es wäre nämlich richtig.
>
> Nein, wäre es nicht. Man kann mit regulären Ausdrück*en* kontextfreie
> Sprachen parsen.

Das will ich sehen. Hier ist eine einfache kontextfreie Sprache (zu
Demonstrationszwecken gleich als Yapp-Grammatik samt kleinem Testscript
implementiert):


# Klammer.yp
%%
s : a { return { s => $_[1] } }
;

a : '(' b ')' { return { a => $_[2] } }
;

b : a b { return { b => [@_[1,2]] } }
| { return 'eps' }
;
%%
__END__


#!/usr/bin/perl
use warnings;
use strict;
use 5.010;

use Klammer;
use Data::Dumper;

my $parser = Klammer->new();

for my $input (
"()",
"(()())",
"(()(()))",
"(()((()))",
"(()(()))()",
"(()()()()()()()()()()()()()()())",
"(((((((((((((((((((((((())))))))))))))))))))))))",
) {
my $value = $parser->YYParse(yylex => sub { $input =~ m/(.)/g; return $1; });

say $input, " ", Data::Dumper->Dump([$value], ['tree']);
}
__END__

Wie lauten die regulären Ausdrücke, mit denen Du diese Sprache parsen
kannst? (Hint: Niemand bezweifelt, dass man in einer
turing-vollständigen Sprache einen Parser dafür schreiben kann)


>> Nur für Perl irrelevant.
>
> Nein, ist es nicht.

Perl kennt keine regulären Ausdrücke im Sinne der Chomsky-Hierarchie.
Warum sollten die relevant sein?

Bernd Nawothnig

unread,
Apr 1, 2012, 9:14:17 AM4/1/12
to
On 2012-03-31, Peter J. Holzer wrote:

> Wie lauten die regulären Ausdrücke, mit denen Du diese Sprache parsen
> kannst? (Hint: Niemand bezweifelt, dass man in einer
> turing-vollständigen Sprache einen Parser dafür schreiben kann)

Da kommt nix mehr, jede Wette :-)




Bernd

--
"Die Antisemiten vergeben es den Juden nicht, dass die Juden Geist
haben - und Geld." [Friedrich Nietzsche]

Thomas 'PointedEars' Lahn

unread,
Apr 4, 2012, 1:52:37 PM4/4/12
to
Peter J. Holzer wrote:

> On 2012-03-28 18:06, Thomas 'PointedEars' Lahn <Point...@web.de> wrote:
>> Peter J. Holzer wrote:
>>> On 2012-03-25 22:18, Thomas 'PointedEars' Lahn <Point...@web.de>
>>> wrote:
>>>> Peter J. Holzer wrote:
>>>>> Thomas 'PointedEars' Lahn wrote:
>>> [kleiner Ausflug in die Welt der formalen Sprachen]
>>>>>
>>>>> Allerdings sind Perl-Regexps keine regulären Ausdrücke im formalen
>>>>> Sinn, sondern eine Erweiterung davon. Insbesondere kann man damit
>>>>> Klammern matchen (Beispiele siehe FAQ seit ewigen Zeiten).
>>>>
>>>> Das ist nur teilweise richtig. Mit der rekursiven Erweiterung in Perl-
>>>> Regexps bzw. PCREs muss für jede zusätzliche Rekursion Speicher
>>>> reserviert werden. Klammersprachen können daher so nur eingeschränkt
^^^^^^^^^^^^^
>>>> geparst werden. man perlre.
>
> Falls Du damit ausdrücken willst, dass man mit endlichem Speicher nur
> eine endliche Schachtelungstiefe bearbeiten kann, so ist das
> trivialerweise richtig, trifft aber IMHO auf jeden Parser zu, der
> beliebige Typ-2-Sprachen parsen können soll.
>
> Falls Du meinst, dass Perl-Regexps ein hartcodiertes Limit hätten, so
> kann ich das der Dokumentation nicht entnehmen. Dort steht zwar:
>
> Recursing deeper than 50 times without consuming any input string
> will result in a fatal error. The maximum depth is compiled into
> perl, so changing it requires a custom build.
>
> Aber beachte die Einschränkung "without consuming any input string".
^^^^^^^^^^^^^
Ach[tm]!

> Ich konnte jedenfalls gerade problemlos den String
>
(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
> matchen - das sind mehr als 50 Ebenen.

Darüberhinaus ist es sinnfrei und hat mit SGML-basierten
Auszeichnungssprachen – das Parsen von HTML war das Thema dieses
(Teil-)Threads – nichts zu tun. Daher "… nur eingeschränkt geparst werden".

>>>>> Die von diversen Leuten gebetsmühlenartig vorgetragene Begründung,
>>>>> dass man mit regulären Ausdrücken keine kontextfreien Sprachen parsen
>>>>> könne, ist daher eine Themenverfehlung.
>>>>
>>>> Ex falso quodlibet. Und ich jedenfalls habe das hier *so* _nicht_
>>>> behauptet.
>>>
>>> Um so schlimmer. Es wäre nämlich richtig.
>>
>> Nein, wäre es nicht. Man kann mit regulären Ausdrück*en* kontextfreie
>> Sprachen parsen.
>
> Das will ich sehen. Hier ist eine einfache kontextfreie Sprache (zu
> Demonstrationszwecken gleich als Yapp-Grammatik samt kleinem Testscript
> implementiert):
>
> […]
> for my $input (
> "()",
> "(()())",
> "(()(()))",
> "(()((()))",
> "(()(()))()",
> "(()()()()()()()()()()()()()()())",
> "(((((((((((((((((((((((())))))))))))))))))))))))",
> ) {
> my $value = $parser->YYParse(yylex => sub { $input =~ m/(.)/g; return
> $1; });
>
> say $input, " ", Data::Dumper->Dump([$value], ['tree']);
> }
> __END__
>
> Wie lauten die regulären Ausdrücke, mit denen Du diese Sprache parsen
> kannst?

/[()]/g

> (Hint: Niemand bezweifelt, dass man in einer
> turing-vollständigen Sprache einen Parser dafür schreiben kann)

Da wir hier in dclpm sind: Nichts anderes – z. B. eine while-Schleife über
die Matches des obigen Ausdrucks mit angeschlossenem Stack und/oder Counter
– habe ich gemeint.

Offensichtlich hast Du nicht nur mindestens keine Ahnung von HTML (in dem –
in Analogie zu kKA – sowohl `(' und `) optional sein können, und das
betreffende Element oder der Attributwert *trotzdem* existiert), sondern
willst mich auch absichtlich missverstehen.

EOD

Peter J. Holzer

unread,
Apr 6, 2012, 5:52:51 AM4/6/12
to
On 2012-04-04 17:52, Thomas 'PointedEars' Lahn <Point...@web.de> wrote:
> Peter J. Holzer wrote:
>> On 2012-03-28 18:06, Thomas 'PointedEars' Lahn <Point...@web.de> wrote:
>>> Nein, wäre es nicht. Man kann mit regulären Ausdrück*en* kontextfreie
>>> Sprachen parsen.
>>
>> Das will ich sehen. Hier ist eine einfache kontextfreie Sprache (zu
>> Demonstrationszwecken gleich als Yapp-Grammatik samt kleinem Testscript
>> implementiert):
[...]
>> Wie lauten die regulären Ausdrücke, mit denen Du diese Sprache parsen
>> kannst?
>
> /[()]/g
>
>> (Hint: Niemand bezweifelt, dass man in einer
>> turing-vollständigen Sprache einen Parser dafür schreiben kann)
>
> Da wir hier in dclpm sind: Nichts anderes – z. B. eine while-Schleife über
> die Matches des obigen Ausdrucks mit angeschlossenem Stack und/oder Counter
> – habe ich gemeint.

<facepalm/>

Thomas 'PointedEars' Lahn

unread,
Apr 6, 2012, 7:15:26 PM4/6/12
to
Eine PCRE, die *relativ* zuverlässig auf ein *nicht-verschachtelbares*
Element mit *nicht-optionalem* Start- und End-Tag in einem *gültigen* HTML5-
Dokument matcht, wäre demnach

/<([a-z]+) # element type name
(?: # attribute (optional)
\s+[a-z]+ # attribute name
(?: # attribute value (optional)
\s*
(?:
=\s*
(?:
"[^"]*"
|'[^']*'
|\S+
)
)?
)?
)*
\s*\/?>
.*?
<\/\1>
/isx

Im Fall von a-Elementen also:

/<(a) # element type name
(?: # attribute (optional)
\s+[a-z]+ # attribute name
(?: # attribute value (optional)
\s*
(?:
=\s*
(?:
"[^"]*"
|'[^']*'
|\S+
)
)?
)?
)*
\s*\/?>
.*?
<\/\1>
/isx

[Übersehen wurde bei dem zuvor vorgeschlagenen Ansatz ausserdem, dass
Zeilenumbrüche im Start-Tag sowie im Elementinhalt erlaubt (und bei längeren
Tags oder Inhalten aufgrund besserer Lesbarkeit sogar empfehlenswert) sind.
Das s-Flag (PCRE_DOTALL) löst dieses Problem.]

HTH
0 new messages