Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> [PCRE] preg_match_all - Fraza w przeczeniu
starach
post
Post #1





Grupa: Zarejestrowani
Postów: 999
Pomógł: 30
Dołączył: 14.01.2007
Skąd: wiesz ?

Ostrzeżenie: (0%)
-----


Witam.
Chciałbym wykorzystać w przeczeniu (^), nie pojedynczy znak tylko frazę.
  1. <?php
  2. $source = 'tak asdfafasf tak saafdafaf tak';
  3. $pattern = '#tak([^tak]*)tak#';
  4. preg_match_all($pattern, $source, $matches);
  5. var_dump($matches);
  6. ?>
Tak jak jest powyżej nie działa. Jak napiszę [^(tak)]* też nie działa.
Więc jak to zrobić ?

Ten post edytował orglee 9.01.2008, 15:37:58
Go to the top of the page
+Quote Post
nevt
post
Post #2





Grupa: Przyjaciele php.pl
Postów: 1 595
Pomógł: 282
Dołączył: 24.09.2007
Skąd: Reda, Pomorskie.

Ostrzeżenie: (0%)
-----


w tym przypadku nie ma potrzeby zaprzeczenia... powinno działać zwykłe:
  1. <?php
  2. $pattern = '/tak(.*?)tak/';
  3. ?>


--------------------

-
Oh no, my young coder. You will find that it is you who are mistaken, about a great many things... -
Go to the top of the page
+Quote Post
starach
post
Post #3





Grupa: Zarejestrowani
Postów: 999
Pomógł: 30
Dołączył: 14.01.2007
Skąd: wiesz ?

Ostrzeżenie: (0%)
-----


Ja wiem że można prościej w tym wypadku, ale ja podałem to tylko jako przykład,
ale i tak nie wiesz nawet jak się cieszę że ktoś odpowiedział :P
Chodzi o wielokrotnie zagnieżdżone tagi.

W manual'u PHP znalazłem taki komentarz użytkownika (http://pl.php.net/manual/pl/reference.pcre...yntax.php#78822)
  1. Dla przykładu, jeżeli chcecie wyciąć element <div>.
  2. Dokładnie, od <div> do odpowiadającego mu elementu </div>.
  3. <?
  4. $str = "<dqiv1>1+<div2>2+<div3><b><c>3</c></b></div3>2-</div2>1-</div1>";
  5.  
  6. preg_match("#<div.> ( ".
  7. " ( (?>[^<]*) ( < ( ([^/d]|d([^i]|i[^v])) | /([^d]|d([^i]|i[^v])) ) )? )* ".
  8.  " | (?R) )* </div.>#xi", $str, $m);
  9. var_dump($m[0]);
  10.  
  11. ?>
  12. Dobiera dokładnie od <div2> do </div2>. I, jeśli zmienicie <dqiv1> na <div1>, dopasuje on od <div1> do </div1>

Wszystko cacy, ale mam jeszcze problem ze zrozumieniem niektórych części wzoru odpowiedzialnego za pobranie tagów.
Mianowicie:
(?>[^<]*) - ???
(?R) ) - ???
I dlaczego po ( < ( ([^/d]|d([^i]|i[^v])) | /([^d]|d([^i]|i[^v])) ) ) jest znak zapytania ?
x - ? w manual'u napisali że ignoruje on spacje dzięki niemu, za wyjątkiem spacji w klasach znaków i poprzedzonych \ i znakiem nowej linii, ale pewien nie jestem więc prosiłbym o powiedzenie mi czy dobrze to rozumiem.
i - żeby olewał wielkość liter, to jeszcze wiem ( chyba :P )

Ten post edytował orglee 9.01.2008, 17:45:52
Go to the top of the page
+Quote Post
nevt
post
Post #4





Grupa: Przyjaciele php.pl
Postów: 1 595
Pomógł: 282
Dołączył: 24.09.2007
Skąd: Reda, Pomorskie.

Ostrzeżenie: (0%)
-----


witam - powiem ci szczerze - sam nie mam zielonego pojecia jak to działa i jak to modyfikować... spotkałem się z przykładami na "rekurencyjne" wyrażenia regularne, ale kompletnie ich nie rozumiem...

natomiast podpowiem ci jak w jednym ze swoich projektów rozwiązałem (a może raczej obszedłem) problem zgnieżdzeń tagów... (konretnie wymiana własnych znaczników na tagi html...)
1. wszystkie wyrażenia dla preg_replace() zprojektowałem tak, że działają tylko dla tagów które nie zawierają wewnątrz innych tagów...
2. a potem, korzytając z tego że preg_replace() zwraca liczbę dokonanych zmian, powtarzam w pętli while() algorytm aż do momentu, kiedy już nic się nie zmienia... to oznacza, że już wszystkie poziomy tagów zostały sparsowane...

prosty przykład
Kod
1. łańcuch startowy: 'xx{jakis tekst yy{inny tekst}yy i jeszcze jeden}xx'
2. po pierwszym przelocie: 'xx{jakis tekst <b>inny tekst</b> i jeszcze jeden}xx'
3. po drugim przelocie: '<p>jakis tekst <b>inny tekst</b> i jeszcze jeden</p>'


oczywiście nie jest to optymalne rozwiązanie - ale przy niewielkich ilościach danych do obróbki świetnie się sprawdza...

powodzenia.


--------------------

-
Oh no, my young coder. You will find that it is you who are mistaken, about a great many things... -
Go to the top of the page
+Quote Post
zimi
post
Post #5





Grupa: Zarejestrowani
Postów: 233
Pomógł: 9
Dołączył: 3.06.2007

Ostrzeżenie: (0%)
-----


Kod
(?>[^<]*)

"znaczy połknij wszystkie znaki różne od < i nie oddawaj" smile.gif
przetestuj:
  1. <?
  2. echo preg_match("@(?>a+)ab@","aaab");
  3. echo preg_match("@(?>a+)b@","aaab");
  4. echo preg_match("@a+ab@","aaab");
  5. ?>

podobną właściwość... to jest w sumie chyba równoważne... a jak nie jest to nie wiem czym się różni będzie miało
  1. <?
  2. echo preg_match("@(?>a++)ab@","aaab");
  3. ?>

regexp jak znajdzie "a+" we wzorcu to najpierw łyka wszystkie "a" które spotka, ale jak się okaże że zaraz po "a+" jest potrzebne "a", to jedno "a" "wypluje" z powrotem, aby można było resztę wzorca dopasować
a++ jak i (?>a+) czy analogiczne konstrukcje nie pozwalają "wypluwać"
mam nadzieje że dość jasno to opisałem.. starałem się...
co do (?R) to jest to rekurencyjne zagnieżdżenie, teoretycznie to wygląda tak że cały zewnętrzny wzorzec (jeśli się nie mylę) powinien zostać wpisany w miejsce (?R)
czyli jeśli twój wzorzec ma postać (to jedynie postać schematyczna... nie ma prawa działać...):
Kod
foo(?R)bar
no to to się rekurencyjnie zagłębia w siebie
Kod
foofoofoofoo(?R)barbarbarbar
itp.
w dokumentacji wyrażeń regularnych jest to dość słabo opisane i może dlatego nie udało mi się napisać regexp-a rekurencyjnego który by działał tak jak bym tego chciał... (trochę to pogięte... albo ja jestem głupi smile.gif )
co do "wykorzystania w przeczeniu całej frazy" to tutaj używamy asercji negatywnych "patrzących do przodu"
  1. <?php
  2. echo preg_match("@^(?!Jestem głupi).+$@","Jestem głupi");
  3. ?>

mimo że pozwalam pojawić się wszystkim znakom => .+
To i tak nie dam sobie wmówić że jestem głupi biggrin.gif:P
generalnie polecam przeczytać dokumentację regexp (w sumie 16 stron) dość dobrze napisana (tylko ta rekurencja jakoś średnio...)
najgorsze jest że wszystko jest podobne a mamy wyrażenia warunkowe, 4 rodzaje asercji, rekurencje, różne operacje na podciągach ciężko zapamiętać jak to się oznacza wszystko smile.gif
co do x masz rację... tutaj autor rozdzielał wszystko spacjami dla łatwiejszego czytania tych wszystkich nawiasów...
jak wywalisz spacje to możesz również wywalić x

PS. jak odkryjesz zasadę działania rekurencji we wzorcu to możesz to opisać smile.gif będę wdzięczny smile.gif

EDIT: męczyłem, męczyłem i wymęczyłem, to działa chyba jak kod który podałeś
  1. <?php
  2. $str = "<div>bbdiv>bbbb</div><div>b<div>bbbb</div>bbb</div>bb</div>";
  3. preg_match_all("@<div>(?(?=</?div>)(?R)|.)+</div>@",$str, $match);
  4. var_dump($match);
  5. $out2 = ob_get_contents();
  6. echo'<pre>';
  7. ?>


Ten post edytował zimi 10.01.2008, 01:23:16
Go to the top of the page
+Quote Post
starach
post
Post #6





Grupa: Zarejestrowani
Postów: 999
Pomógł: 30
Dołączył: 14.01.2007
Skąd: wiesz ?

Ostrzeżenie: (0%)
-----


Wczoraj w akcie desperacji wysłałem do autora tego komentarza mejla odpowiedź która przyszła wyjaśniała założenia tego wzorca niestety w bardzo ubogi sposób. Dziękuję zimi wreszcie zaczynam łapać o co chodzi.
Zrozumiałem coś takiego:
Rekurencja działa od środka to znaczy w przypadku tagów pobiera najpierw
Cytat
$str = "<div>bbdiv>bbbb</div><div>b<div>bbbb</div>bbb</div>bb</div>";

czyli część środkową potem wkleja to w miejsce (?R) i sprawdza znowu. Czyli działa od środka na zewnątrz mam rację ?
Teraz sprawa twojego wzorca. Wynik jaki otrzymuję jest identyczny jak w przykładzie z komentarza, teraz prosiłbym o powiedzenie mi czy dobrze go rozumiem.
"@<div>(?(?=</?div>)(?R)|.)+</div>@"
@<div> - bez przesady to chyba rozumiem tongue.gif
@<div>(? - jeśli dopasowałeś <div> sprawdzaj dalej
@<div>(?(?=</?div>) - Kolejna część ma się równać </?div> natomiast w tym znak zapytania sprawdza czy zostało dopasowane </ i jeśli tak każe kontynuować
(?R) - Wklej tutaj to co będzie wynikiem (?(?=</?div>)(?R)|.)+
Dobra a teraz gdzie popełniłem błąd ? hehe

Ten post edytował orglee 10.01.2008, 13:55:42
Go to the top of the page
+Quote Post
zimi
post
Post #7





Grupa: Zarejestrowani
Postów: 233
Pomógł: 9
Dołączył: 3.06.2007

Ostrzeżenie: (0%)
-----


tzn. Twoja forma tłumaczenia do mnie nie dociera (nie mniej wydaję mi się że bardzo źle zinterpretowałeś), ogólne założenie że rekurencja działa od środka jest mylne
jak wywołujesz rekurencje w swoich programach to masz coś a'la
  1. <?
  2. function foo()
  3. {
  4. //jakiś kod
  5. if(...)
  6. {
  7.  //jakiś kod
  8.  foo();
  9.  //jakiś kod
  10. }
  11. //jakiś kod
  12. }
  13. ?>

więc działa od zewnątrz wgłąb(sic!) a nie od środka na zewnątrz
jak nie miałeś doczynienia z rekurencją to zobacz na wikipedii... => odrób lekcje smile.gif
czyli analizując moją regexp:
Kod
@<div>(?(?=</?div>)(?R)|.)+</div>@

@<div> - wiadomo...
(?(?=</?div>)(?R)|.) dla uproszczenia => (?(a)b|c)
w takiej postaci to znaczy jeśli a to dopasuj b w przeciwnym wypadku c
a => (?=</?div>)
b => (?R)
c => .
w wyrażeniu sprawdzającym chyba najczęściej używa sie asercji
Cytat
Kolejna część ma się równać </?div> natomiast w tym znak zapytania sprawdza czy zostało dopasowane </ i jeśli tak każe kontynuować

chciałbym szczególną uwagę zwrócić na słowo dopasowane... w regexp kojarzy się ono z tym że jak to uroczo jest w dokumentacji opisane regexp "skonsumował" jakiś ciąg
tutaj jednak mamy doczynienia z asercją, asercja sprawdza co jest w odpowiednim miejscu (zależnie od tego jakiej asercji użyjemy) jednak jej nie konsumuję...
prosty przykład
  1. <?php
  2. preg_match("@^(?=a)b$@", "ab");
  3. ?>

zwróci false bo asercja nie dopasuję "a", a jedynie sprawdzi czy "a" znajduję się w żądanym przez asercje miejscu

rekurencje już opisałem... może niezbyt wyraźnie...
Cytat
co do (?R) to jest to rekurencyjne zagnieżdżenie, teoretycznie to wygląda tak że cały zewnętrzny wzorzec (jeśli się nie mylę) powinien zostać wpisany w miejsce (?R)

a zatem jeśli w regexp znajdzie się (?R) to parser (czy interpreter... nie wiem jak się przyjęło to nazywać) będzie dopasowywał od początku wyrażenie regularne...
może przykład
Cytat
<div>bb<div>bbbb</div><div>b<div>bbbb</div>bbb</div>bb</div>

wyrażenie dopasuje <div> tak jak to stwierdziliśmy i ponieważ dalej od miejsca w którym zaczyna nie widzi <div> lub </div> dopasowuję w kółko jeden jakikolwiek znak...
gdy zobaczy <div> to ten fragment jest jakby odcinany i dopasowuję do niego od nowa całego regexp-a
więc wycina sobie pogrubiony fragment:
Cytat
<div>bb<div>bbbb</div><div>b<div>bbbb</div>bbb</div>bb</div>

i niejako sam w sobie robi:
  1. <?
  2. preg_match("@<div>(?(?=</?div>)(?R)|.)+</div>@","<div>bbbb</div>");
  3. ?>

tutaj bez problemu przechodzi po kolejnych "b" dopasowując po jednej literce
a dalej napotyka problem bo mimo że ma dopasować jakąkolwiek literkę to nie może tego zrobić gdy ta literka zaczyna ciąg: </div>
więc "+" zakończył swoje działanie i dopasowywany jest </div> z końcówki wyrażenia
po czym wyskakuje z niego na wyższy poziom i dopasowuję reszte która mu została z całości:
Cytat
<div>bb<div>bbbb</div><div>b<div>bbbb</div>bbb</div>bb</div>

mam nadzieję że dość jasno wytłumaczyłem...

zakończyłem swój wykład o regexp smile.gif może kiedyś napiszę kurs bo jest słabo opisany w sumie
jednak polecam przyswoić dokumentacje do której jest link w manualu PHP

Ten post edytował zimi 10.01.2008, 18:20:23
Go to the top of the page
+Quote Post
starach
post
Post #8





Grupa: Zarejestrowani
Postów: 999
Pomógł: 30
Dołączył: 14.01.2007
Skąd: wiesz ?

Ostrzeżenie: (0%)
-----


Oj wiem co to jest rekurencja. Stosowałem ją wielokrotnie, chociażby przy generatorze wielopoziomowego menu.
Już nie rób ze mnie AŻ takiego głąba zimi tongue.gif
Fakt faktem że dokumentacje do PCRE przeczytałem tylko pobieżnie zwracając właściwie tylko uwagę na modyfikatory.
Dobra rozumiem teraz, on nie idzie od środka tylko od lewej do prawej czyli jak zwykle. Jeszcze raz dziękuję.
Rzecz jasna dla opu panów klikam na pomógł i żałuję że mogę kliknąć tylko raz.

Ten post edytował orglee 10.01.2008, 23:11:13
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Aktualny czas: 19.08.2025 - 17:32