Autor Zpráva
obi
Profil
Ahoj,
narazil jsem v regulárních výrazech na docela zásadní problém, jde o tohle:
Chci dejme tomu najít v textu řetězec začínající slovem "START", následovaný čímkoli kromě slova "ahoj" a končící slovem "END".

<?php
$text = "nějaký začátek START blabla ahoj ble END a nějaký konec";  

preg_match_all("#START .*(?!ahoj) END#U", $text, $matches);

print_r($matches);
?>
^ zkoušel jsem to napsat pomocí záporného tvrzení o následujícím, ale řetězec "START blabla ahoj ble END" se s tímto reg.výrazem stejně shoduje (což nechci, obsahuje slovo "ahoj")

Prosím o radu a předem děkuji :-)
nightfish
Profil
Napadá mě jenom udělat to "dvouprůchodově".
<?php
$text = "nějaký začátek START blabla ahoj ble END a nějaký konecSTART neco neco END neco";  
preg_match_all("#START(.*)END#U", $text, $matches, PREG_SET_ORDER);
foreach ($matches as $v) {
  if (!preg_match('#ahoj#', $v[1])) {
    print_r($v);    
  }
}


Ale třeba přijde někdo s něčím lepším.
Joker
Profil
nightfish:
Když dvouprůchodově, tak v tom druhém už bych použil jen strstr, tam není potřeba regulární výraz.
obi
Profil
Bohužel tohle potřebuju kvůli daleko složitějšímu problému a dělat to dvou-průchodově by znamenalo strašné znepřehlednění (pokud by to vůbec v tomhle případě šlo použít)

Musí existovat i jiný způsob, určitě na tak základní věc nemohli tvůrci reg. výrazů zapomenout :-(
obi
Profil
Tak jsem na něco přišel :-)

<?php //tohle už funguje
$text = "nějaký začátek START blabla ahoj ble END a nějaký konec";

preg_match_all("#START (?!.*ahoj).* END#U", $text, $matches);

print_r($matches);
?>
^ řetězec nevyhoví, protože obsahuje slovo 'ahoj'. Když 'ahoj' smažu, reg.výraz mi najde vše od START po END :-)


------
Bohužel ale nastal další problém:
<?php //v tomto řetězci nevyhovuje žádná jeho část, přestože 'ahoj' je až za slovem 'END'
$text = "nějaký začátek START blabla ble END a nějaký ahoj konec";

preg_match_all("#START (?!.*ahoj).* END#U", $text, $matches);

print_r($matches);
?>
Radek9
Profil
obi:
Mně funguje tohle:
$text = "nějaký začátek START blabla ble END a nějaký ahoj konec START blabla blesdgsdg END";
preg_match_all("#START (?!ahoj*).* END#U", $text, $matches);
print_r($matches);
obi
Profil
Radek9:
Mně funguje tohle:
>
preg_match_all("#START (?!ahoj*).* END#U", $text, $matches);

tomuhle odpovídá i řetězec "něco START blabla ahoj ble END něco jiného", právě jsem to zkoušel
Radek9
Profil
obi:
Pravda, zkusím dál testovat.
AM_
Profil
Radek9:
proč mezera po START a před END? navíc to znamená: [START] [mezera] [něco jiného než ahojjjjjjjjjjjj...] [jakékoli znaky] [mezera] [END]

šel bych spíš tímto směrem:
preg_match_all("#START(?!.*?ahoj.*?).*?END#U", $text, $matches);

ale problém zřejmě bude např. START blabla END ahoj, protože po START to najde ahoj. Sice až za END, ale nevím, jestli se dá ta lookahead podmínka nějak omezit jen po to END
obi
Profil
tak jsem zase o krok dál
<?php //tohle požadovaný řetězec najde
$text = "něco START aa bb END ahoj konec";

preg_match_all("#START (?!.*ahoj.*END).* END#U", $text, $matches);

print_r($matches);
?>


<?php //ale v tomto řetězci už je problém
$text = "něco START aa bb END prostředek ahoj START cc ahoj dd END konec";

preg_match_all("#START (?!.*ahoj.*END).* END#U", $text, $matches);

print_r($matches);
?>
...doufal jsem že přepínač U tomuto problému zamezí, ale ne :-/
nightfish
Profil
Joker:
Když dvouprůchodově, tak v tom druhém už bych použil jen strstr, tam není potřeba regulární výraz.
Svatá pravda. (I když pak tedy spíš strpos než strstr.)

obi:
Bohužel tohle potřebuju kvůli daleko složitějšímu problému a dělat to dvou-průchodově by znamenalo strašné znepřehlednění (pokud by to vůbec v tomhle případě šlo použít)
Tak nás netahej za nos a napiš, k čemu to potřebuješ. Řešit tady příliš zjednodušenou variantu nemá smysl.
obi
Profil
nightfish: oki, klidně, i když i tahle zjednodušená varianta by mě zajímala ;-)
Chci si vytvořit SQL parser, který mi z SQL dotazu vytáhne názvy tabulek.

Vstup:
SELECT *, field1 as muj_sloupec, field2 
FROM tabulka_3 z, (SELECT * FROM tabulka_1) x JOIN tabulka_2 y ON x.jmeno=y.id
WHERE field3='val1' AND field4=5


Výstup:
array(
  [0] => tabulka_3, 
  [1] => tabulka_1,
  [2] => tabulka_2
)
AM_
Profil
No, pokud ti jde jen o názvy tabulek, tak stačí všechny znaky platné pro název tabulky za FROM ;) ale budeš muset asi rozlišit dva případy, jednak názvy bez apostrofů (menší výčet povolených znaků) a názvy v apostrofech (od prvního apostrofu k dalšímu).

Pokud ale chceš dotaz nějak důkladněji analyzovat, vykašlal bych se na regulární výrazy a napsal si sekvenční syntaktický analyzátor - není to legrace, ale výsledek bude lepší, než přes reguláry, přecejen na syntaktyckou analýzu stavěné moc nejsou...
nightfish
Profil
Názvy tabulek se mohou vyskytovat jenom na určitých místech - v [#12] je to za FROM a za JOIN. Stejně tak mohou názvy tabulek obsahovat jenom určité znaky.
Přibližně by to mohlo vypadat takto:
~(FROM|JOIN)\s([^\s]+)[\s,]~s // název tabulky uveden napřímo, končí první čárkou nebo bílým znakem
~(FROM|JOIN)\s`(.*)`~sU // název tabulky uzavřen ve zpětných apostrofech


Pokud by bylo potřeba s tím kouzlit víc, asi by stálo za to si někde najít/napsat vlastní SQL parser (i když na tohle by stačilo udělat jenom tokenizer).
EDIT: jejda, psal jsem tak dlouho, až jsem napsal totéž co AM_
Nox
Profil
obi teda výslovně neřek, že to smí být jen selecty...

k tomu by přibylo (asi nekompletní seznam)
ALTER TABLE [table]
INSERT INTO [table]
UPDATE [table]
LOCK TABLES [table]
DROP [table]
RENAME TABLE [table]
SHOW (FULL) TABLES [table]
...

Což by teda nebylo až tak kritický, ale...

nightfish:
...navíc:
INSERT INTO table1 (header) VALUES ('NEWS FROM BRNO')

=> table1, BRNO

Takže ten parser by fakt byl asi třeba

Když jsem hledal jak zajistit, že výraz není v uvozovkách, našel jsem jen nějaký složitý regexp, kde se počítalo zda následují páry uvozovek (a do toho by se muselo ještě přidat escapování)
takže asi kopec srandy
obi
Profil
nightfish:
~(FROM|JOIN)\s([^\s]+)[\s,]~s

tohle funguje pro jednoduché dotazy, ale např. tabulku 'tabulka_2' bychom tímto stylem v následujícím dotazu hledali těžko:
SELECT * FROM (SELECT neco FROM tabulka_1 WHERE id=5), tabulka_2


zkusím teda na netu pohledat něco o tvorbě toho "sekvenčního syntaktického analyzátoru" a o "tokenizeru" (vůbec nevím co to je a jak to má fungovat).
AM_
Profil
[#16] obi
to je pravda, moc jsem nepromyslel složitější dotazy...
no jde o to, že si znak po znaku (resp. token po tokenu) čteš ten příkaz, pamatuješ si aktuální stav (jaký příkaz a kterou jeho část zrovna načítáš) a ukládáš to do nějaké rozumné struktury...
obi
Profil
AM:
no jde o to, že si znak po znaku (resp. token po tokenu) čteš ten příkaz, pamatuješ si aktuální stav (jaký příkaz a kterou jeho část zrovna načítáš) a ukládáš to do nějaké rozumné struktury...

To zní složitě :-D Kdybych uměl napsat regulární výraz
SELECT cokolivNeobsahujici(FROM) FROM cokolivNeobsahujici(WHERE|ORDER|GROUP|LIMIT)

byla by to otázka jednoho regulárního výrazu (pro select) a jednoho cyklu. Takže jsem zase u mojí původní otázky, jak ten regulární výraz 'cokolivNeobsahující(něco)' napsat...
AM_
Profil
SELECT `FROM`, `ahoj` FROM test

asi se té syntaktické analýze opravdu nevyhneš. Ano, je to složité, ale přes reguláry je hotové peklo pokrýt všechny případy..
obi
Profil
AM:
no jde o to, že si znak po znaku (resp. token po tokenu) čteš ten příkaz, pamatuješ si aktuální stav (jaký příkaz a kterou jeho část zrovna načítáš) a ukládáš to do nějaké rozumné struktury...

vůbec si nedokážu představit, jak by takový algoritmus v PHP vypadal... a na netu jsem taky nic nenašel

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm:

0