Zvláštní chování jednoho REGEXPU

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Martin Mates
Člen | 179
+
0
-

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

ic
Člen | 430
+
0
-

Je trochu zvláštní udávat regulár výčetm znaků, které nemůže obsahovat

to pak mohou projít i nějaké nechtěné tvary např: Velká B÷ŚtřiÇe

Teoreticky ale myslím že za popisované chování může [:punct:] to by nemělo zahrnotat diakritiku nebo jo?

DocX
Člen | 154
+
0
-

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 Form. Nebo použít mb_ereg_match, ten ale zase neumí Perl reguláry. Nebo udělat výraz nějak srozumitelněji. No prostě v PHP je cesta po Unicodu zapeklitá.

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
+
0
-

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.

Viz http://www.czso.cz/…ke_republice

Jan Jakeš
Člen | 177
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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ů:

  1. 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.
  2. 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
+
0
-

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 | 7790
+
0
-

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.

Jan Jakeš
Člen | 177
+
0
-

A nevyplatilo by se tedy, kdyby u preg_* funkcí byl v Nette modifikátor u napevno?