Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: wyrażenie regularne
Forum PHP.pl > Forum > PHP
nospor
Mam taki ciąg
Kod
START bla bla bla END inneblabla inneblabla

Chcę zamienić wszystko między START a END na coś innego, włącznie ze START i END.
No to piszemy proste wyrażenie:
  1. $text = 'START bla bla bla END inneblabla inneblabla';
  2. $text2 = preg_replace('/^START\s.*?\sEND/','#zamienione#',$text);
  3. echo $text2;

W rezultacie otrzymamy:
Kod
#zamienione# inneblabla inneblabla

Proste.
Sprawa się komplikuje, gdy pomiędzy START i END włożym podSTART i pod END
Kod
START bla bla bla START blablaW END bla3 END inneblabla inneblabla

W wyniku naszego wyrażenia otrzymamy
Kod
#zamienione# bla3 END inneblabla inneblabla

A powinniśmy otrzymać to samo co w pierwszym przypadku.
No to powiecie: zamien .*? na .* i po sprawie. No nie do końca, bo jest jeszcze jeden przypadek, ze START i END pojawi nam się też rownożędnie do pierwszego, czyli np:
Kod
START bla bla bla START blablaW END bla3 END inneblabla START ccc xxx END inneblabla

Zrobie .* da nam
Kod
#zamienione# inneblabla

a mi chodzi, by uzyskac
Kod
#zamienione# inneblabla START ccc xxx END inneblabla


Podsumowując:
ma być zamienione tylko pierwsze główne START END, nawet jeśli ma w sobie inne START END, ale nie można już ruszych późniejszych START END
Idzie to zrobić wyrażeniem regularnym?
kreciko
Musisz w środku tego wyrażenia dodać bardzo podobne, które może wystąpić 0 lub więcej razy. Nie wiem czy dobrze zapisałem:

Kod
^START\s(START\s.*?\sEND)*\sEND
nospor
Juz myslalem ze bedzie dobrze ale nie
  1. $text = 'START bla bla bla START blablaW END bla3 END inneblabla START ccc xxx END inneblabla';
  2. $text2 = preg_replace('/^START\s.*?(START\s.*?\sEND)*.*?\sEND/','#zamienione#',$text);
  3. echo $text2;

Nadal zwraca mi
Kod
#zamienione# bla3 END inneblabla START ccc xxx END inneblabla

Gdy zamienie (START\s.*?\sEND)* na (START\s.*?\sEND)+ to zwraca juz dobrze, ale tylko dla przypadku, gdy te wewnetrzne START END istnieje. Gdy nie będzie istnialo, znowu się wykrzaczy
kreciko
  1. $text2 = preg_replace('/^START.*(START.*END)*.*END/','#zamienione#',$text);
nospor
kreciko ale wowczas łyknie też tego ostatniego END a pisałem ze tego nie chce smile.gif
wookieb
preg_replace_callback i trochę rekurencyjnych wywołań.
Nie możesz się posługiwać operatorami kontrolującymi chciwość np (?)
nospor
Cytat
Nie możesz się posługiwać operatorami kontrolującymi chciwość np (?)
To pytanie czy stwierdzenie?
wookieb
"?" to operator leniwego wybierania znaków
nospor
To ja wiem, pytam sie ciebie czy twoj ostatni post to pytanie czy stwierdzenie smile.gif
Bo jak przejrzysz kody to zauwazysz, ze uzywam leniwego wybierania znaków wiec nie rozumiem sensu twojego zdania
Cytat
Nie możesz się posługiwać operatorami kontrolującymi chciwość np (?)
Wiec sie pytam czy to pytanie czy to stwierdzenie smile.gif
wookieb
Stwierdzenie, że nie możesz go używać snitch.gif
nospor
why, wookieb why?
wookieb
Dobra może powiedz mi po co chcesz to zrobić? Wiesz, że mój parser bbcode można użyć do innych parserów zmieniając tylko ustawienia?

A dlaczego nie możesz, ponieważ wtedy preg_replace_callback nie będzie w stanie rekurencyjnie wyszukać START i END w tekście bo nigdy nie wystąpi 2x END oraz 2x START w dopasowanym ciągu.
kreciko
Cytat(wookieb @ 28.09.2010, 10:08:45 ) *
Nie możesz się posługiwać operatorami kontrolującymi chciwość np (?)

To pytanie chyba było skierowane do mnie, ponieważ ja je(operator chciwości) pominąłem z powodu niewiedzy. Mea culpa.
nospor
Cytat
Dobra może powiedz mi po co chcesz to zrobić?
for money winksmiley.jpg
No jak to po co? Zeby działało smile.gif

Cytat
A dlaczego nie możesz, ponieważ wtedy preg_replace_callback nie będzie w stanie rekurencyjnie wyszukać START i END w tekście bo nigdy nie wystąpi 2x END oraz 2x START w dopasowanym ciągu.
Ok. Moze się pobawie tym callbackiem
wookieb
Cytat(nospor @ 28.09.2010, 10:33:16 ) *
for money winksmiley.jpg
No jak to po co? Zeby działało smile.gif

To może tak. Jaką funkcjonalność chcesz stworzyć?
nospor
Taką jak opisałem.

Obszedłem to troszke i mam rozwiązanie
  1. $text = 'START bla bla bla START blablaW END bla3 END inneblabla START ccc xxx END inneblabla';
  2. $text = preg_replace('/^\s*START\s/s','',$text);
  3. $text = preg_replace('/START(\s+.*?\s+)END/s','#START#\\1#END#',$text);
  4. $text = 'START '.$text;
  5. $text = preg_replace('/^START\s+.*?\s+END/s','#zamienione#',$text);
  6. $text = str_replace(array('#START#','#END#'),array('START','END'),$text);
  7. echo $text;
Noidea
@nospor
Twój kod sypnie się, jeśli START END będzie zagnieżdżone więcej niż 2 razy. Jeśli się to nigdy nie zdarzy, to OK, ale jeśli ma to działać w każdych warunkach, to lepiej jest wykorzystać tak zwane "recursive subpatterns". Działa to tak samo jak zwykła rekurencja (?R), tyle że nie wstawia tam całego oryginalnego wyrażenia, tylko n-ty nawias okrągły. A zapis jest taki: (?1), (?2), (?n)

  1. <?php
  2.  
  3. $text = 'START aa START bb START cc START dd END c END b START cc2 END b2 END a END inneblabla START xxx START yyy END zzz END inneblabla';
  4.  
  5. echo preg_replace( "~^(START\b((?1)|.)*?\bEND\b)~s", "#zamienione#", $text );
  6.  
  7. ?>
nospor
Cytat
wój kod sypnie się, jeśli START END będzie zagnieżdżone więcej niż 2 razy. Jeśli się to nigdy nie zdarzy, to OK
Tak, wiem o tym. Z założenie nie będzie jednak takiej sytuacji

Cytat
to lepiej jest wykorzystać tak zwane "recursive subpatterns". Działa to tak samo jak zwykła rekurencja (?R), tyle że nie wstawia tam całego oryginalnego wyrażenia, tylko n-ty nawias okrągły. A zapis jest taki: (?1), (?2), (?n)
Działa wyśmienicie, dzięki smile.gif
Muszę poczytać o tych rekursywnych wyrażeniach bo próbuje przetrawić ten kod co podales i za chiny go nie kumam smile.gif
Jeszcze gdzie jakiego backdora mi wlozyles winksmiley.jpg

edit: i już chyba kumam smile.gif

Z ciekawości zrobiłem testy czasowe.
Wyrażenie regularne jest szybsze od moich "kwiatków" smile.gif
Noidea
Ogólnie to:
1: Dopasuj START
2: Najpierw spróbuj dopasować całe zagnieżdżone START(...)END (rekurencja, GOTO 1: ), a jeśli się nie uda to:
3: Dopasuj jeden znak (kropka)
4: Jeśli napotkałeś END to zakończ, w przeciwnym razie GOTO 2:


EDIT:
No ty w swoich kwiatkach też masz wyrażenia regularne smile.gif
W dodatku ślamazarność wyrażeń to trochę mit. To znaczy często, jeśli porównujemy regexp z całą masą substr w pętli to okazuje się, że ciągłe kopiowanie stringa w inne miejsca w pamięci daje taki narzut, że wyrażenia okazują się szybsze smile.gif
nospor
Cytat
No ty w swoich kwiatkach też masz wyrażenia regularne
No tak.... czy już dziś piątek czy jak :/
Methestel
Chcesz robić walidacja poprawności tekstu (czy liczba START-ów jest równa licznie END-ów)?
nospor
Nie wiem jak bardzo trzeba po chinsku czytac by dojsc do takiego wniosku winksmiley.jpg
Poza tym wydaje mi się, iż jestem na takim etapie iż wiem jak policzyć w tekście liczbę dwóch słów i porównać te liczby ze sobą winksmiley.jpg
Problem juz rozwiązany
Methestel
Nie doszedłem do takiego wniosku tylko zapytałem czy nie potrzebujesz walidacji bo niedawno sam musiałem zmierzyć się z problemem niepoprawnie zbudowanego tekstu ze znacznikami (np częściowo ucięty XML). Przepraszam, że chciałem podzielić się doświadczeniami :/
nospor
Cytat
Przepraszam, że chciałem podzielić się doświadczeniami
No ale chyba nie będziesz latał teraz po różnych tematach i nie pytał każdego czy nie chce usłyszeć jak zrobić coś tam bo akurat to przerabiałeś winksmiley.jpg

Nie zrozum mnie źle: dzielenie się doświadczeniem to fajna sprawa i jak najbardziej to popieram ale rób to we właściwym miejscu i nie wyskakuj ni stąd ni zowąd ludziom w tematach z czymś takim. Poza tym źle zrozumiałem Twojego posta i myslalem że ty źle zrozumiales i wymyslasz jakieś inne rozwiązanie winksmiley.jpg
Methestel
Nie będe
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.