Zvláštní chování jednoho REGEXPU
- Martin Mates
- Člen | 179
Zdravím. Píšu regulární výraz pro kontrolu zadaného města. Lehce jsem editoval regulární výraz pro jméno/přijmení z http://www.regexp.cz/.
/^[^ßĚŮěů0-9[:space:][:punct:]][^0-9[:punct:]]{1,20}$/
Výraz jsem na stránce regexp odzkoušel pro PHP a funguje to v pořádku. V Nette formulari se to chová ale trošku jinak. Mluvím samozřejmě o validaci na straně serveru.
- Klášterec – ANO
- Klášterec nad – ANO
- Klášterec nad Ohri – ANO
- Klášterec nad Ohří – NE
Nechápu to. Na stránkách regexp.cz to poslední taky projde. Nevím, kde bych mohl hledat chybu. Napadlo mě, že Nette strká ten řetězec regulárnímu výrazu nejak encodovaný, tak jsem zkusil na konci dát {1,50} a také nic.
Díky za nápady
- DocX
- Člen | 154
ic napsal(a):
Teoreticky ale myslím že za popisované chování může[:punct:]
to by nemělo zahrnotat diakritiku nebo jo?
[:punct:]
= Any punctuation character: ! ' # S % & ' ( )
* + , – . / : ; < = > ? @ [ / ] ^ _ { | } ~
Nicméně co já jsem to zkoušel, tak mi to nefunguje ani přímo:
<?php
$val = 'Klášterec nad Ohří';
echo preg_match('/^[^ßĚŮěů0-9[:space:][:punct:]][^0-9[:punct:]]{1,20}$/', $val);
?>
A je to tím, že text je kódován v UTF-8 a ve skutečnosti ten regulár
porovnává něco jako KlĂĄĹĄterec nad OhĹ�Ă
a zde
konkrétně to ř
, tedy �
, nesedí nutnosti
nemít :punct:
No jo, ale co s tim? Napadá mě převést text před porovonáním do
nějakého single-byte kódování, to by ale potřebovalo zásah do
No prostě
v PHP je cesta po Unicodu zapeklitá.Form
. Nebo použít mb_ereg_match
, ten ale zase neumí
Perl reguláry. Nebo udělat výraz nějak srozumitelněji.
Tak jsem zkoušel to mb_ereg_match
a zdá se, že je to
normální PCRE. Akorát se pattern neuzavírá do //
. Takže bych
to vyřešil nahrazením preg_match
za mb_ereg_match
ve funkci validateRegexp
v souboru
Nette/Forms/Controls/TextBase.php
;)
BTW: Co má ten regulár vůbec dělat? Podle mě je naprosto k ničemu. Jen snad kontroluje první písmeno a to, že tam není tečka. Ale třeba „spojku“ ‚ž‘, ‚l‘ apod. v např. ‚Město ž Les‘ to veme :D. Když už to checkovat, tak pořádně ;)
Editoval DocX (4. 10. 2009 12:10)
- vlki
- Člen | 218
Jen připomenu, že pokud je to regulár, který má filtrovat názvy obcí, pak by měla být maximální délka nastavena na 33.
- Jan Jakeš
- Člen | 177
Tedy, když tu na to koukám… Co bych k tomu dodal:
Za prvé, jak už tu zmínilo hodně lidí, takhle by regulár na kontrolu
názvů měst opravdu neměl vypadat.
Pokud jde o to, proč se to nechová, jak bys očekával, opravdu bude na
vině kódování. (Znak ř
to teda podle mě nebude, ale to je
teď uplně jedno.) Konkrétně zde je asi na vině délka řetězce v jiném
kódování. Nevěřím, že ti to s {1,50}
neprošlo. Podle mě to
projde už s {1,21}
, a můžeš klidně zkusit
{1,}
.
Ani to ale není tak důležité, podstatné je, že kvůli kódování vidí
funkce preg_match()
jiné znaky a jinak dlouhý řetězec.
Napravit by to mělo jít snadno, modifikátorem /u
:
$val = 'Klášterec nad Ohří';
echo preg_match('/^[^ßĚŮěů0-9[:space:][:punct:]][^0-9[:punct:]]{1,20}$/u', $val);
Schválně si do tohohle zkus dát délku {1,17}
, mělo by to
projít. (Ten řetězec má sice 18 znaků, ale první znak je specifikován
v první části regexpu.)
- Martin Mates
- Člen | 179
Díky všem za reakce. Máte pravdu, ten regulár je více méně na nic, už jsem ho nahradil vlastním. Jen mě to zajímalo proč to neprošlo. Testoval jsem to a na vině je skutečně písmeno ř, takže nejspíš má pravdu DocX, díky.
Nevěřím, že ti to s {1,50} neprošlo.
Co k tomu říct. Že mi nevěříš, s tím nic neudělám. Sám si to vyzkoušej, abys uvěřil. Modifikátor /u vyzkouším, díky.
Nakonec jsem ten regulár na města změnil na:
/^[a-zA-ZáčďéěíňóřšťúůžýÁČĎÉĚÍŇÓŘŠŤÚŮŽÝ ]{1,33}$/
- Jan Jakeš
- Člen | 177
Co k tomu říct. Že mi nevěříš, s tím nic neudělám. Sám si to vyzkoušej, abys uvěřil. Modifikátor /u vyzkouším, díky.
OK, sorry. Teď jsem to vyzkoušel v různých kódováních a mě to
prostě vždy projde už s {1,21}
. Je to jedno… DocX měl pravdu
v tom, že je to kódováním a s tím já jsem souhlasil. Modifikátor
/u
nastavuje podporu dlouhých Unicode znaků.
Ještě zvaž, jestli do výčtu znaků nepřidat některé slovenské nebo přehlasované znaky (nevím, jestli se v ČR v názvech objevují…).
- Martin Mates
- Člen | 179
Juan napsal(a):
Co k tomu říct. Že mi nevěříš, s tím nic neudělám. Sám si to vyzkoušej, abys uvěřil. Modifikátor /u vyzkouším, díky.
OK, sorry. Teď jsem to vyzkoušel v různých kódováních a mě to prostě vždy projde už s
{1,21}
. Je to jedno… DocX měl pravdu v tom, že je to kódováním a s tím já jsem souhlasil. Modifikátor/u
nastavuje podporu dlouhých Unicode znaků.Ještě zvaž, jestli do výčtu znaků nepřidat některé slovenské nebo přehlasované znaky (nevím, jestli se v ČR v názvech objevují…).
Tak jsem to ještě jednou zkusil. V Nette formuláři mi tohle nevezme Klášterec nad Ohří. Možná dělám něco špatně, nevím.
<?php
->addRule(Form::REGEXP, 'Špatně zadané město.', '/^[^ßĚŮěů0-9[:space:][:punct:]][^0-9[:punct:]]{1,50}$/')
?>
Když za to přidám /u
, tak už to vezme i s {1,21}. Díky
moc, neznal jsem. Je to zvláštní, možná, když to předhodíš jen funkci
regexp, tak to projde. Jak jsem psal – na stránce regexp.cz, kde si můžeš
zkoušet výrazy, to projde přesně, jak říkáš.
Díky za point se slovenskými znaky, přidám to tam. To mě nenapadlo.
- DocX
- Člen | 154
Hele, to /u
je vážně přesně na UTF-8, jaktože jsem o tom
nevěděl :-] Díky i ode mě za to :)
Pro ty, kteří o tom nevěděli jako já, přidávám odkaz na možné modifikátory v PCRE funkcích PHPka
Jen mě tak napadá, nebylo by dobré, kdyby to Nette přidávalo samo? Stejně je přece stavěné na to, že všechno je v UTF-8?
Editoval DocX (4. 10. 2009 14:27)
- Jan Jakeš
- Člen | 177
Martin Mates: Já to právě přímo s třídou
Form
nezkoušel, nechtělo se mi to psát :) Zkoušel jsem to jen
na preg_match()
. Takže možná to bude něčím takovým… Anebo
jsme prostě někde něco měli v jiném kódování, nevím.
DocX: To možná vůbec není špatný nápad. Záleží,
jak je Nette v tomto směru stavěno. Například třída Form
podporuje také jiná kódování, která lze explicitně nastavit. Koukal jsem
se ale do zdrojáků a pokud je řetězec v jiném kódování, vždy se
napřed převede na UTF-8. Tzn. Nette pak opravdu pracuje POUZE s UTF-8 a zde
by to tedy nemělo dělat žádné problémy…
Jo a myslel si to tak, že by se to zabudovalo pouze do kontroly formulářů nebo centrálně do Nette (třeba podobně jako má Nette vlastní funkce na práci s řetězci)? Ono asi záleží, na kolika místech Nette se někam zadává regexp…
- kravčo
- Člen | 721
Martin Mates napsal(a):
Nakonec jsem ten regulár na města změnil na:
/^[a-zA-ZáčďéěíňóřšťúůžýÁČĎÉĚÍŇÓŘŠŤÚŮŽÝ ]{1,33}$/
Súhlasím s tým, že takýto regulárny výraz na kontrolovanie názvu obce je dosť zbytočný. Dovoľuje zadať nespočetné množstvo nezmyselných názvov, vyberiem:
q
aaaaaaaaaa
aaaaaaaaab
aaaaaaaaac
...
zzzzzzzzzx
zzzzzzzzzy
zzzzzzzzzz
DocX napsal(a):
Jen mě tak napadá, nebylo by dobré, kdyby to Nette přidávalo samo? Stejně je přece stavěné na to, že všechno je v UTF-8?
Modifikátory by Nette rozhodne nemalo nastavovať samo, implicitné nastavenie by znižovalo možnosti písania výrazov…
- Jan Jakeš
- Člen | 177
kravčo: Tenhle regulár je o dost lepší, než ten původní. Záleží, jestli potřebuje kontrolovat pouze povolené znaky nebo zda jde opravdu o název. Pro první možnost ten regulár bohatě stačí. Druhá by byla o dost komplikovanější.
Modifikátory by Nette rozhodne nemalo nastavovať samo, implicitné nastavenie by znižovalo možnosti písania výrazov…
To určitě, na druhou stranu ten modifikátor /u
pro hodně
univerzální použití má pár neduhů:
- When the subject string contains invalid UTF-8 sequences / codepoints, it basically result in a „quiet death“ for the preg_* functions, where nothing is matched but without indication that the string is invalid UTF-8.
- PCRE regards five and six octet UTF-8 character sequences as valid (both in patterns and the subject string) but these are not supported in Unicode.
Obojí by šlo řešit kontrolou regexpu i stringu před zavoláním preg_*
funkce. A otázkou je, zda by se takto upravené preg_* funkce nevyplatilo
zabalit podobně jako je třída Nette\String
nebo třeba jako
extras…
- kravčo
- Člen | 721
Juan napsal(a):
kravčo: Tenhle regulár je o dost lepší, než ten původní. Záleží, jestli potřebuje kontrolovat pouze povolené znaky nebo zda jde opravdu o název. Pro první možnost ten regulár bohatě stačí. Druhá by byla o dost komplikovanější.
To, o koľko je tento lepší ako pôvodný, vôbec neriešim.
A ešte dodám, že ste možno zabudli na Frýdek-Místek…
- David Grudl
- Nette Core | 8227
kravčo napsal(a):
Modifikátory by Nette rozhodne nemalo nastavovať samo, implicitné nastavenie by znižovalo možnosti písania výrazov…
Ono by to zrovna v tomto případě opodstatnění mělo, protože stejný výraz se používá i v JavaScriptu a tam je zase utf-8 automaticky.