Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Wyrażenia regularne - warunek, dany ciąg znaków ma nie występować
Forum PHP.pl > Forum > PHP
Sebusik
Witam po raz kolejny dzisiaj.

Taki problem z wyrażeniami (znowu...). Chcę postawić warunek, aby dany ciąg znaków nie występował. Coś w ten deseń:
  1. ([^abc]*)

Powyższy kod daje warunek, aby w ciągu nie występowały litery abc w obojętnej kolejności. Mi chodzi oto, aby w ciągu nie występowało np. imię Alina. Jak to zrobić?

Pozdrawiam.
Noidea
  1. <?php
  2.  
  3. $txt = "imie: Ala, imie: Piotr, imie: Alina, imie: Jan";
  4.  
  5. preg_match_all( "~imie: (?!Alina)([^,]+)~", $txt, $matches );
  6. print_r( $matches );
  7.  
  8. ?>


Technika ta nazywa się przewidywaniem negatywnym. Więcej informacji - http://gajdaw.pl/php/wyrazenia-regularne-p...torial/p23.html
by_ikar
Podczepię się trochę pod temat, chodź nie dokładnie o to mi chodzi, ale mniej więcej w ten deseń i może będzie możliwość zrobienia tego. Do rzeczy, próbuje skleić wyrażenie, które będzie szukało klamer, oczywiście proste {(.*?)} działa jak należy, ale za dużo pochłania. Postanowiłem że umieszczę jakiś znacznik ucieczki, powiedzmy {ignore}{/ignore} i wszystko co by znalazło się pomiędzy {ignore} nie będzie dopasowywane do wyrażenia. I w sumie w tym mam problem, nie wiem jak skleić taki warunek w wyrażeniu. Szukałem gdzieś w sieci jakiegoś info, ale nie bardzo rozumiem jak miałbym to złożyć i czy jest w ogóle taka możliwość. Jakby ktoś się orientował, wdzięczny będę za pomoc.
Noidea
Możesz najpierw usunąć wszystkie {ignore}...{/ignore} a później szukać w tym co zostało. Będzie działać dobrze, o ile nie będziesz zagnieżdżał jednych {ignore} w drugich.

  1. <?php
  2.  
  3. $txt = "asd {a} afsda {b} asd {ignore} asdsa {c} asdasd {d} asdasd {/ignore} asdasd {e} asdas";
  4.  
  5. $tmpTxt = preg_replace( "~{ignore}.*?{/ignore}~i", "", $txt );
  6. preg_match_all( "~{(.*?)}~", $tmpTxt, $matches );
  7. print_r( $matches );
  8.  
  9. ?>



Ewentualnie, jeśli nie możesz sobie pozwolić na to, żeby wycinać tekst pomiędzy {ignore}:
  1. <?php
  2.  
  3. $txt = "asd {a} afsda {b} asd {ignore} asdsa {c} asdasd {d} asdasd {/ignore} asdasd {e} asdas";
  4.  
  5. preg_match_all( "~{(?:(ignore}.*?{/ignore)|(.*?))}~", $txt, $matches );
  6. print_r( $matches );
  7.  
  8. ?>

To czego szukasz masz pod indeksem 2. Musisz tylko odfiltrować puste wartości
Sebusik
@Noidea
No coś mi to średnio działa. Ten kod:
  1. ([^abc]*)

Robi tak, że w nawiasie znajduje się ciąg, do momentu, aż wystąpi litera a, b lub c, a wyrażenie nie jest regularne. Żeby wyjaśnić jaśniej to powiedzmy mamy taki ciąg:
  1. Andrzej, Jolanta, Wiesława, Piotrek, Alina, Adam, Damian

I teraz jak wykorzystać przewidywanie, aby w 1 nawiasie mieściły się wszystkie znaki przed imieniem Alina, a w 2 wszystko co jest po?

Edit: Trochę źle podałem z tym przykładem abc. Powinno być tak:
  1. ([^a]*)a(.*)

Tutaj też na oko robiłem, ale założenie jest takie, że w 1 nawiasie jest wszystko to, co wystąpiło przed a, a w 2 nawiasie jest wszystko to, co wystąpiło po tej literce a. Jak to przerobić, żeby zamiast A było jakieś słowo?
by_ikar
@Noidea ten drugi przykład jest w miarę doby, nie jest idealny taki jaki bym chciał, ale musi mi wystarczyć wink.gif kombinowałem nad czymś takim:

Kod
(?<={ignore}).*?(?={/ignore})


przewidywanie pozytywne, zacząłem w ten sposób żeby mi znajdowało to co chcę, a następnie zamienię to na negatywne, tyle że o ile w kiedy chcę znaleźć dany ciąg to ładnie znajduje a kiedy ustawiam na przewidywanie negatywne wtedy wrzuca mi w tablicę każdy znak osobno. Nie znam się aż tak na wyrażeniach, coś tam ich liznąłem, ale bardziej zaawansowane techniki są dla mnie czarną magią. Ogólnie chciałem to załatwić jednym wyrażeniem, że w preg_replace() ładuje sobie w tablicy wyrażenia, oraz w drugiej tablicy treść na którą miałoby to być zmieniane. Robię taki swój system szablonów, który w sumie już teraz całkiem dobrze się sprawuje, tyle że jak przyszło mi do wrzucenia w taki szablon jakiegoś kawałka javascript to niestety sparsował i javascript, dlatego myślę nad jakimś sposobem i w sumie jest kilka opcji, z czego blok z ignore byłby całkiem dobrym wyjściem.. Dlatego jeżeli na tyle się znasz i jesteś w stanie mi pomóc, to fajnie by było jakby to jakoś umieścić w jednym wyrażeniu, myślałem nad czymś takim:

Kod
#?((?<!{ignore}).*?(?!{/ignore}){(.*?)})#x


Tyle że to niestety nie zadziała i wywali błąd parsera.. Próbowałem jeszcze mniej więcej w taki sposób to ugryźć:

Kod
{?((?!.*ignore)(.*?))}


I prawie łapie pierwszy cały blok ignore, z tym że 2 już pomija: http://regexr.com?2uc6p

Jeżeli rozumiesz o co mi chodzi i wiesz jak to wykonać, byłoby super gdybyś mi pomógł wink.gif

P.S. sorki @Sebusik że się tak wpierniczyłem z buciorami do tematu..

EDIT:
Sprawa jest już nie aktualna, całość mojego problemu rozwiązało takie wyrażenie:

Kod
{(?(?!.*ignore)(.*?))}


Najzwyczajniej wcześniej źle zabierałem się za sam warunek, dlatego rozwalało mi cały string na pojedyncze znaki. Efekt działania można obejrzeć tutaj: http://regexr.com?2uc6s jeżeli ktoś kiedyś będzie się borykać z podobnym problemem co ja smile.gif Dzięki za wszelką pomoc! smile.gif
Noidea
@by_ikar Coś to twoje wyrażenie nie za bardzo chce działać: http://regexr.com?2uc7h smile.gif

Być może da się to załatwić jednym wyrażeniem regularnym, tylko po co, skoro o wiele łatwiej wspomóc się kodem PHP http://ideone.com/d6s8F


@Sebusik Nie chodzi ci czasem o explode? smile.gif
  1. <?php
  2.  
  3. $txt = "Andrzej, Jolanta, Wiesława, Piotrek, Alina, Adam, Damian";
  4. list( $przedAlina, $poAlinie ) = explode( "Alina", $txt );
  5.  
  6. echo $przedAlina;
  7. echo "<br>";
  8. echo $poAlinie;
  9.  
  10. ?>
by_ikar
ehh w sumie nie sprawdzałem czy coś może być przed.. A już się cieszyłem że skończyłem wink.gif tak wiem że mógłbym to w ten sposób załatwić, dostawiając gdzieś preg_replace_callback tyle że tak jak pisałem, najpierw spróbuje zrobić to na jednym wyrażeniu a do tego czasu będzie to alternatywne wyjście. Nie mniej, dzięki za wskazanie bubli! smile.gif
Sebusik
@Noidea
Właśnie nie bardzo, bo chodzi o to, że Alina to tylko przykład, za Alinę to wchodzi mi wyrażenie regularne.

Może napiszę co chcę osiągnąć, a może przez inny sposób będzie to szło zrobić łatwiej.
Mam sobie system templatek. Gdy chcę sobie jakąś templatkę pobrać do zmiennej robię tak:
  1. eval('$templatka = "'.$templates->get('nazwa').'";');

Denerwujące jest to, że nawet najmniejszą zmianę muszę robić warunkiem php i wczytywać inną templatkę (np. sprawdzanie, czy w menu dany link jest aktywny). Postanowiłem więc dopisać sobie takie warunki templatkowe:
  1. <div class="costam">
  2. Jakiś kod {if $a == true} <u>HTML</u> {else} <b>HTML</b> {/if}
  3. </div>

I teraz jest taki problem, że przed dodaniem tego zawsze całą templatke przerzucałem jeszcze przez addslashes(), teraz tak zrobić nie mogę, bo kod {if}{/if} tych slasherów mieć nie może (mogę ew. robić tak, że całość będę slasherował, a potem z kodu {if}{/if} będę to usuwał). Do wyszukiwania zrobiłem takie wyrażenie regularne:
  1. ([^{]*)\{if([^}}*)]([^{]*)\{\/if\}([^{]*)
  2. //tutaj jeszcze nie dodałem obsługi else

I właśnie wadę one ma taką, że na początku jest warunek daje tylko, żeby nie występował znak "{", a nie "{if", więc wszystko działa dobrze, dopóki w kodzie nie pojawi się jakiś znak { użyty inaczej niż warunek, bo potem to się sypie.

Jakiś pomysł, jak to ugryźć?
by_ikar
Więc twój problem również dotyczy templatków biggrin.gif cóż, powiem ci tak, prócz samej zamiany klamerek ({ oraz }) na odpowiednio <?php oraz ?> nie musisz mieć wyrażeń które będą ci parsować to co jest wewnątrz klamer żeby np móc korzystać z warunków, pętli itp. Bo pętle i blok if posiadają swoją alternatywną formę zapisu:

if:
Kod
if(warunek):
//..
elseif(warunek2):
//..
else:
//...
endif


while:
Kod
while(warunek):
//..
endwhile


foreach:
Kod
foreach($arr as $key => $val):
//..
endforeach


for:
Kod
for($i=0; $i <= 10; $i++):
//...
endfor


switch:
Kod
switch ($i):
    case 0:
        echo "i equals 0";
        break;
    case 1:
        echo "i equals 1";
        break;
    case 2:
        echo "i equals 2";
        break;
    default:
        echo "i is not equal to 0, 1 or 2";
endswitch


nie wiem szczerze mówiąc jak zapisać pętle do-while, ale tego mało kiedy się używa więc idzie przeżyć smile.gif nie mniej masz caluteńką funkcjonalność czyli funkcje, pętle, warunki wszystko co potrzeba. Do tego trzeba dorobić trzeba jedynie tak jak ja sobie wymyśliłem, blok ignore, dzięki któremu będziemy mieli jakąś drogę ucieczki smile.gif pewnie nie wszyscy taki sposób popierają, lecz jest to niemal to samo co umieszczenie prawie samego php, tyle że zamiast pisać <?php .... ?> wstawiamy dany kod w klamry i to wszystko. Wygląda to nieco bardziej estetyczniej, i nie jest pozbawione jakoś funkcjonalności że trzeba coś sobie samemu dorabiać..
Fifi209
ikar tylko po co wynajdywać koło na nowo? Masz OPT czy Smarty od tego
by_ikar
Spodziewałem się tego typu odpowiedzi, ale ja mam na to swoją odpowiedź. Otóż smarty jakie jest każdy wie, opt jest lepsze, nowa wersja którą się szykuje będzie lepsza, tyle że jest to język w języku. Nie chcę się uczyć kolejnych języków, nie chcę systemu templatków który waży mniej więcej 200kb, nie chciałbym poznawać dodatkowych dziesiątków plików i klas żeby zrozumieć jak to działa. Dlatego postanowiłem rozwiązać to w swój sposób i raczej wątpię aby ktoś mnie przekonał do używania już istniejących systemów tempatków mimo że mogą być na prawdę świetne i rewolucyjne, ponieważ jak już wspomniałem, to jest język w języku a w tego typu udziwnienia nie chcę się bawić. I jak już miałbym nie wynajdować koła na nowo, to użył bym czystego php bez żadnych templatków.
Sebusik
@by_ikar
Problem jest taki, że po sparsowaniu tego wyglądało by to tak:
  1. <?php
  2.  
  3. $template = '<a href="link.php" <?php if($costam == true) ?> class="active" <?php endif; ?>>Link</a>';
  4.  
  5. ?>

Wątpię, żeby to działało potem...
Fifi209
Fakt, może jest to tak jak piszesz ale tylko po części. Ty masz poznać tylko klasy, a ktoś kto zajmuje się tworzeniem szablonów, rozmieszczeniem elementów martwi się o resztę, w przypadku podanej przez Ciebie metody nie może przy szablonach pracować ktoś kto nie zna PHP. Poza tym "język" szablonów jest bardzo prosty, nauczysz się go w jeden dzień, daje Ci to dodatkową przewagę - idziesz do roboty, dostajesz projekt z systemem szablonów i nie musisz się zastanawiać jak on działa, bo używałeś go mnóstwo razy a tak? Zrobisz po swojemu, przyjdzie ktoś inny i będzie musiał przepisać kod tak, aby trzymało się to kupy i aby był łatwy do modyfikacji w przyszłości.

@up

eval - ale to zły pomysł ;]
Sebusik
Dlaczego? I jaka alternatywa? Tylko żebym nie musiał przekazywać danych do templatek ręcznie:
  1. $templatka = $templates->get('nazwa', array('zmienna' => $zmienna, 'inna_zmienna' => $inna->zmienna));


PS: Prawdopodobnie znalazłem rozwiązanie swojego problemu - używałem "(.*)" co połykało mi cały kod, więc jak było kilka warunków, to licho to działało, ale używając "(.*?)" działa to normalnie. Aktualnie to tylko teoretycznie mam, bo odkryłem to przed chwilą, a pogoda za oknem w końcu jest normalna, więc to musi poczekać wink.gif
by_ikar
Um, no wiesz, smarty/opt/twig i cała reszta systemów szablonów nie narzuca swojej składni, nie narzucają jakichś swoich funkcji itp.. Do użycia pętli, czy warunków wystarczą podstawy php, czyli mniej więcej takie samo jakie jest wymagane przy użyciu innych systemów szablonów smile.gif to jest akurat kiepski powód, dla którego powinienem używać szablonów. Dzięki temu że szablon nie ma swojej składni, tylko jest to zwyczajna składnia php, to wątpię aby w php się zmieniło jakieś używanie pętli czy warunków do tego stopnia że obecny sposób zapisu pętli przestałby działać. Pracujesz nad 2 projektami, oba są różne, oba wykorzystują inny system szablonów, musisz wówczas znać składnie obu systemów szablonów, a tutaj nie musisz znać, wystarczy że znasz podstawy php których można się równie szybko nauczyć jak podstawy składni szablonów, z tą różnicą, że jej uczysz się raz, a szablony mają to do siebie, że każdy autor szablonów robi to na inne kopyto, dzięki czemu musisz znać składnie opt, smarty, twig, phptal i to najlepiej w każdej wersji, bo w wersji 2 zmienili kilka rzeczy smile.gif jak dla mnie błędne koło.

@Sebusik, jeżeli przypiszesz to do zmiennej, to jak najbardziej wygląda to beznadziejnie, sam rozwiązuje to inaczej, bo nic nie przypisuje do zmiennej dlatego u mnie to się sprawdza..
Sebusik
Co do postu #10 - niestety to nie przejdzie, bo po sparsowaniu by wyglądało tak:
  1. $zmienna = "Jesteś ".if($wiek >= 18):"pełnoletni".else:."niepełnoletni".endif.".";

A to nie bardzo ma prawo działać, więc odpada. Poza tym - nikomu swojej składni nie narzucam, bo mam zamiar dodać tylko warunki, a poza tym robię do dla siebie i wątpię, żebym to kiedyś komuś udostępnił wink.gif
Co do twojego ostatniego zdania -> nie rozumiem, jak możesz nic nie przypisywać nic do zmiennej, rozwiń trochę, albo jakiś przykład.
A i co do tego eval - wciąż czekam na alternatywę.
by_ikar
Nie przypisuje layoutu do zmiennej żeby potem go wyświetlić. Wyświetlam, a właściwie includuje już skompilowane pliki szablonu, w sumie cache tego szablonu. Niby dobrze, niby źle, są różne szkoły. Chciałem uniknąć eval, chciałem uniknąć echo, więc zostałem przy include.

Też mam warunki, tylko przykładowo u mnie szablon po skompilowaniu wygląda tak powiedzmy:

Kod
<?php $this->includeComponent('layout', 'head'); ?>

    <h1><?php echo $this->get('naglowek') ?></h1>
    <p><?php echo $this->get('message') ?></p>
    <p>
    <?php if($this->get('records')->rowCount()): ?>
        <?php foreach($this->get('records')->fetchAll() as $row): ?>
            ID: <?php echo $row['id'] ?>; name: <?php echo $row['name'] ?>; data: <?php echo $row['data'] ?><br>
        <?php endforeach ?>
        <?php $this->get('records')->closeCursor() ?>
    <?php else: ?>
        Brak wyników.
    <?php endif ?>
    </p>
    
<?php $this->includeComponent('layout', 'stopka'); ?>


tpl:

Kod
{include_component('layout', 'head')}

    <h1>{@$naglowek}</h1>
    <p>{@$message}</p>
    <p>
    {if($records->rowCount()):}
        {foreach($records->fetchAll() as $row):}
            ID: {@$row.id}; name: {@$row.name}; data: {@$row.data}<br>
        {endforeach}
        {$records->closeCursor()}
    {else:}
        Brak wyników.
    {endif}
    </p>
    
{include_component('layout', 'stopka')}


Sam nie wiem czy dobre to jest podejście, czy złe, nie mniej bardzo wygodne, z czego pokazywałem kilku osobą które w sumie nawet nie dotykały smarty czy opt i musiałem wytłumaczyć tylko kilka rzeczy i sam szablon im się podobał.
Sebusik
Trochę rozmyślałem nad tym i postanowiłem, że chcę zostać przy eval() (po to, aby trzymać templatki w bazie danych). Wymyśliłem, że stworzę funkcję, które będą odpowiadały za warunki (funkcja(warunek, spełniony, niespełniony)), pętle, itd.. Już po napisaniu większości dzisiaj, przed chwilą sobie uświadomiłem, że bez większych problemów można połączyć twoje rozwiązanie z moim...


Btw. Jeżeli ktoś ma pomysł, jak pominąć eval() to z chęcią wysłucham wink.gif
Fifi209
Użyć jakiegoś systemu szablonów typu OPT czy smarty?
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.