perl regex többszörös selector

Üdv!

A perlre doksi szerint ha egy selector többször is értéket kap akkor az utolsó-t lehet kiolvasni belőle...
Egy bonyolultabb kifejezésben lenne szükségem arra hogy minden értékét megkapjam (mondjuk egy array-ba vagy ilyesmi) anélkül hogy szét kellene split-elnem a stringet.

Tehát pl ha ez után:

$text = 'aaa bbb ccc';
$text =~ /(?:(\w+)\s*)*/;

$1 értéke ['aaa', 'bbb', 'ccc'] lenne 'ccc' helyett

Hozzászólások

Én valahogy így oldanám meg:

($temp = $text) =~ s/(\w+)\s*/$eredmeny[$i++]=$1/ge;

Valoszinuleg a

/g

modifiert keresed, probald ki ezt:


$ perl -lE '$text = "aaa bbb ccc"; @matches = ( $text =~ m/\w+/msg ); say for @matches'

Egy kis modositassal hasznalhato arra is:


$ perl -lE '$text = "aXXa bYYb cZZc"; @matches = ( $text =~ m/\w(\w+)\w/msg ); say for @matches'

update:
Azt hiszem felfogtam, mit is akarsz elerni:


$ perl -lE '$text = "aaa bbb ccc"; $text =~ m/ (?: (\w+) (?{ push @matches, $1 }) \s* ) * /xms; say for @matches'

Hali!

Igazából a végeredmény így néz ki: http://hup.pastebin.com/m50998dd1

Viszont kérdeznék még :)
$+ micsoda?
a fenti kód mér ad ilyen warningokat?
Variable "%attrs" will not stay shared at (re_eval 574) line 1.
egyébként hibátlanul működik :)

ilyen bb szerü tag-eket olvasgatok ki vele:
[valami]
[valami=ertek]
[valami attr=ertek]
[valami attr="érték"]
...

és a többszörösség ott jön a képbe hogy több attribútum is lehet.

Köszi a segítséget!

Kezdjuk az egyszerubbel:

A

$+

valtozo a legutolso matchelo capturing zarojel tartalmara hivatkozik, bovebben lasd

perldoc perlvar

.

The text matched by the last bracket of the last successful search pattern. This is useful if you don’t know which one of a set of alternative patterns matched. For example:

/Version: (.*)|Revision: (.*)/ && ($rev = $+);

Az emlitett warning legegyszerubb elofordulasa valami ilyesmi:


#! /usr/bin/perl

use strict;
use warnings;
#no warnings 'closure';

sub foo {
  my $var = 0;
  sub bar {
    ++$var;
    print "bar: $var\n";
  }
  ++$var;
  print "foo: $var\n";
}

bar();
foo();
__END__

A kimenet:


Variable "$var" will not stay shared at ./hup.id.83056 line 10.
bar: 1
foo: 1

Azaz ha egymasba agyazott nevesitett szubrutinokat hivsz, akkor a belso sub a kulsoben lexikalis valtozokat nem abban az allapotban latja, amiben a kulso (azaz "not stay shared"). Ez azert van, mert a nevesitett sub nem lexikalis, igy a bar() mar akkor is definialt, amikor a foo() meg le sem futott. Epp ezert ezt ugy szoktak elkerulni, hogy a beagyazott sub mindig legyen nevtelen (

sub {}

), es igy lexikalis.

Ha biztos vagy benne, hogy a warning nem jelez valodi hibat, akkor elnyomhatod a kikommentelt

no warnings 'closure'

-rel.

Szoval ennek fenyeben gondold vegig, hogy mi van a linkelt kodreszlet elott es utan. (Ha jol sejtem egy nevesitett sub()-ban van az egesz pastebinen levo kod.)

Aztan figyelj arra is, hogy a

perldoc perlre

szerint experimental a

(?{  })

konstrukcio, ha jol tudom, fokepp a kulonbozo lexical scoping issuek nem kielegito kezelese miatt.

Ezt a problemat ugy lehet megkerulni, hogy nem lexikalis valtozot hasznalsz (azaz

my %attrs

helyett

our %attrs

), es a

(?{  })

konstrukcion belul pedig mindig lokalizalod a hasznalt valtozokat, hogy a regex engine backtrackingje se kavarjon be.

De javaslom inkabb olvasd el ezeket, mert fokepp az elsoben sok minden tisztabb, mint ahogy en probalom magyarazni:
egy nagyon hasznos thread a perlmonks.org-rol
perldoc (?{ })
perldoc use re eval

Tovabba en a helyedben azert megneznem a CPAN-on levo BBCode-Parser-t kozelebbrol, mert valoszinuleg sokkal konnyebben olvashato es karbantarthato kodot tudnal irni azt felhasznalva.

Tegnap ota atgondoltam alaposabban, hogy milyen problemak lehetnek a kododdal. Amit az elozo hozzaszolasban irtam az alapvetoen jo, de kisse osszemostam ket kulonbozo problemat.

1) Az elso gond az lehet, amit a "will not stay shared" warning jelez. A megoldas az, hogy a

(?{})

blokkban ne hasznalj soha lexikalis valtozokat, hanem helyettuk package global valtozokat.

Vagyis alapesetben:


$refText = 'foo[tag param="value"]';
our %attrs;
$$refText =~ ...

Viszont ha a kodod olyan kornyezetben van, hogy ismetlodoen futhat (azaz tobb mint egyszer, peldaul egy sub{}-ban, amit sok helyrol hivsz), akkor az our keves, es erre van szukseg:


sub parse_bb_tag {
  $refText = \shift;
  local our %attrs;
  $$refText =~ ...
}

Ha nem hasznalod a localt, akkor a korabbi parse_bb_tag() hivasok key/value ertekei is szerepelni fognak az %attrs hashben.

2) regex engine backtracking vs.

(?{})

Ezt lusta leszek leirni, de a mar korabban is linkelt threadben itt talalsz jol ertheto peldat arrol, hogy mi a gond, es megoldast is ra. Ha ez meg nem lenne eleg egyertelmu, akkor olvasd hozza a perlre dokumentaciobol azt a bekezdest, ami igy kezdodik: The "code" is properly scoped in the following sense...