Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Solution + rapide pour autoriser seulem ent certains caracteres

13 réponses
Avatar
Olivier Masson
Bonjour,

pour filtrer des caractères indésirables, ou plutôt indiquer quels sont
ceux autorisés, j'utilise :

if (ereg('[^[:alnum:]\.&-$]',$chaine))
echo 'PAS BON';
else
echo 'OK';

Dans ce cas, pour n'autoriser que les chiffres, lettres ainsi que ., &,
- et $.

Déjà, je n'ai pas réussi à remplacer ereg par une instruction preg.
Ensuite, je me demandais si, avec des instructions de type str il
n'était pas possible de faire le même test plus rapidement.

Merci pour vos éventuelles suggestions.

10 réponses

1 2
Avatar
Olivier Miakinen

pour filtrer des caractères indésirables, ou plutôt indiquer quels sont
ceux autorisés, j'utilise :

if (ereg('[^[:alnum:].&-$]',$chaine))
echo 'PAS BON';
else
echo 'OK';

Dans ce cas, pour n'autoriser que les chiffres, lettres ainsi que ., &,
- et $.


Tout de suite deux remarques :
1) le caractère « . » n'est pas un caractère spécial dans un répertoire
de caractères entre crochets. Il est donc inutile de l'échapper avec
un « » ;
2) inversement, la syntaxe « &-$ » signifie « n'importe quel caractère
compris entre « & » et « $ » (dans l'ordre ascii). Il est possible
que les fonctions ereg et preg traitent différemment ce cas invalide,
faisant ce que tu veux pour ereg et pas pour preg, ce qui pourrait
expliquer pourquoi tu n'y arrives pas avec preg_match.

Par ailleurs, je n'aime pas trop me reposer sur la syntaxe [:alnum:],
par peur (probablement irrationnelle) qu'elle soit dépendante de la
locale utilisée. Tout ceci pour dire que moi j'écrirais ton test comme
suit : [^a-zA-Z0-9.&$-].

Avec ereg :
if (ereg('[^a-zA-Z0-9.&$-]',$chaine))

Avec preg_match :
if (preg_match('/[^a-zA-Z0-9.&$-]/',$chaine))

Ensuite, je me demandais si, avec des instructions de type str il
n'était pas possible de faire le même test plus rapidement.


Peut-être avec <http://fr2.php.net/manual/fr/function.strcspn.php> mais
je ne suis pas sûr que le gain soit très important.

define("LOWER", "abcdefghijklmnopqrstuvwxyz");
define("UPPER", "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
define("SPECIAL", "0123456789.&$-");

...

if (strcspn($chaine, LOWER . UPPER . SPECIAL))
echo 'PAS BON';
else
echo 'OK';


Voire :
if (strcspn($chaine,
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.&$-"))
echo 'PAS BON';
else
echo 'OK';

--
Olivier Miakinen
Troll du plus sage chez les conviviaux : le nouveau venu, avec
son clan, s'infiltre dans les groupes de nouvelles. (3 c.)

Avatar
Olivier Miakinen
Le 04/05/2006 21:07, j'écrivais :

2) inversement, la syntaxe « &-$ » signifie « n'importe quel caractère
compris entre « & » et « $ » (dans l'ordre ascii). Il est possible
que les fonctions ereg et preg traitent différemment ce cas invalide,


J'ai oublié de préciser que la syntaxe [&-$] était invalide du fait
que « & » se trouve après « $ ». La syntaxe [$-&], en revanche, est
parfaitement valide, mais elle est équivalente à [$%&] (et non pas
à [$&-]).

faisant ce que tu veux pour ereg et pas pour preg, ce qui pourrait
expliquer pourquoi tu n'y arrives pas avec preg_match.


Voilà.

--
Olivier Miakinen
Troll du plus sage chez les conviviaux : le nouveau venu, avec
son clan, s'infiltre dans les groupes de nouvelles. (3 c.)

Avatar
Olivier Masson

Tout de suite deux remarques :
1) le caractère « . » n'est pas un caractère spécial dans un répertoire
de caractères entre crochets. Il est donc inutile de l'échapper avec
un « » ;


Ok. Je l'avais echappé car il m'avait semblé que ça ne fonctionnait pas
sans.

2) inversement, la syntaxe « &-$ » signifie « n'importe quel caractère
compris entre « & » et « $ » (dans l'ordre ascii). Il est possible
que les fonctions ereg et preg traitent différemment ce cas invalide,
faisant ce que tu veux pour ereg et pas pour preg, ce qui pourrait
expliquer pourquoi tu n'y arrives pas avec preg_match.



Mon test était donc faussement concluant car le signe - devait se
trouvait entre & et $. Comme dans l'autre message tu dis que c'est
toutefois invalide puisque $ est avant &, le comportement doit être un
peu imprévisible.

Par ailleurs, je n'aime pas trop me reposer sur la syntaxe [:alnum:],
par peur (probablement irrationnelle) qu'elle soit dépendante de la
locale utilisée. Tout ceci pour dire que moi j'écrirais ton test comme
suit : [^a-zA-Z0-9.&$-].



Alors que moi j'aime bien utiliser les [::] car ça améliore la
lisibilité ; par contre ça me fait peur de ne pas échapper des $, - et . :)


Avec preg_match :
if (preg_match('/[^a-zA-Z0-9.&$-]/',$chaine))


Voilà, j'ai donc opté pour preg_match('/[^[:alnum:].&$-]/',$a) ;)
Merci.


Peut-être avec <http://fr2.php.net/manual/fr/function.strcspn.php> mais
je ne suis pas sûr que le gain soit très important.


Bien vu, je n'avais jamais remarqué cette fonction.

Avatar
Olivier Masson
A ma grande surprise (surtout avec ce qu'on lit partout sur ereg/preg),
sur un bench de 100000 itérations avec des chaînes aléatoires de 10
caractères (pareil pour 20), le gain pour preg_match n'est pas toujours
positif ! Lorsqu'il est (souvent tout de même), c'est assez faible
(quelques %).
Quant à strcspn, c'est toujours plus lent (10 à 20%).
Avatar
Olivier Miakinen

A ma grande surprise (surtout avec ce qu'on lit partout sur ereg/preg),
sur un bench de 100000 itérations avec des chaînes aléatoires de 10
caractères (pareil pour 20), le gain pour preg_match n'est pas toujours
positif ! Lorsqu'il [l']est (souvent tout de même), c'est assez faible
(quelques %).


La regexp en question est très simple et se traite sans aucun
aller-retour possible. Je ne suis donc pas surpris que la différence
entre les deux soit faible.

Je suppose qu'il en irait différemment avec un test du style
"(abc|bcd)", sans parler de "(a+b*|ac+b*|c*b+a)". Surtout si ta
chaîne est pleine de a, de b et de c.

Quant à strcspn, c'est toujours plus lent (10 à 20%).


Là encore, rien de surprenant, puisque l'on remplace un test du genre
« si >= 'a' et <= 'z' » par un du genre « si == 'a' ou == 'b' ou == 'c'
ou == 'd' ou ... ou == 'y' ou == 'z' ».

--
Olivier Miakinen
Troll du plus sage chez les conviviaux : le nouveau venu, avec
son clan, s'infiltre dans les groupes de nouvelles. (3 c.)

Avatar
Thomas Harding
Le 04-05-2006, Olivier Miakinen <om+ a écrit :
Par ailleurs, je n'aime pas trop me reposer sur la syntaxe [:alnum:],
par peur (probablement irrationnelle) qu'elle soit dépendante de la
locale utilisée. Tout ceci pour dire que moi j'écrirais ton test comme
suit : [^a-zA-Z0-9.&$-].


Je ne sais pas s'est vrai en PHP, mais avec sed, a-zA-Z autorise aussi
les caractères accentués ET (curieusement) l'espace:
[...]
~tom$ dd if=/dev/random of=motus_clef bsQ2 count=2
~tom$ CLEF=$( sed -e 's/[^a-zA-Z0-9]//g' motus_clef )
~tom$ echo $CLEF
~tom$ óýÚÔÕìåßãèÖkÅAÌqéö9U3DäURlIÚrÇòÿ qcòRÝÁï½ÏZ9kíôÝOJòÕqu
ÀbFмÊhÿîmvÍÕCÆíú0çÔMQm7bWúhCí9z1ÕPgÖV9Rðq¼kçÀé31OçFzOuôGÎ2v
RXU˨ÅÔÛIðNúcá
~tom$ CLEF=${CLEF:0:32}
[...]

(debian Sarge,GNU sed version 4.1.2)

les 2 premières lignes et la dernière sont extraites d'un script
d'install de sites, et destinées à générer une clef de hachage md5.

Je n'ai pas trouvé de solution pour éviter le fichier tampon, of=-
écrivant littéralement dans le _fichier_ «-».
--
Thomas Harding

Avatar
Olivier Miakinen

Je ne sais pas s'est vrai en PHP, mais avec sed, a-zA-Z autorise aussi
les caractères accentués ET (curieusement) l'espace:


Je n'ai *jamais* vu ça sur aucun Unix.

[...]
~tom$ dd if=/dev/random of=motus_clef bsQ2 count=2
~tom$ CLEF=$( sed -e 's/[^a-zA-Z0-9]//g' motus_clef )
~tom$ echo $CLEF
~tom$ óýÚÔÕìåßãèÖkÅAÌqéö9U3DäURlIÚrÇòÿ qcòRÝÁï½ÏZ9kíôÝOJòÕqu
ÀbFмÊhÿîmvÍÕCÆíú0çÔMQm7bWúhCí9z1ÕPgÖV9Rðq¼kçÀé31OçFzOuôGÎ2v
RXU˨ÅÔÛIðNúcá
~tom$ CLEF=${CLEF:0:32}
[...]


Même sous cygwin, il fait ce qu'on lui dit, en l'occurrence ne garder
que les caractères ascii 'a' à 'z', 'A' à 'Z' et '0' à '9'. Bien sûr,
comme sed fonctionne ligne par ligne, il garde aussi les sauts de ligne.

----------------------------------------------------------------------
omiak$ sed -e 's/[^a-zA-Z0-9]//g' motus_clef
bIRMcp0nQWnlyKOv03n5H7b6NY5I6iEiTiqcRS4ZFe33Fv1vTPiYGCV17vDUFvxDtnr5
HdXaUdkLpne15mVJ0Kq0u01ZAjGlhS3Chl371vElQXoW7voI50fDb58GbiPxTmBybh

u
7vxVSCtpXM5SfgEZGFZW8
eZNREt3p3B7W5ko4IEqnqb03ib8c0s
ICmkhAChG4Wg3wZYMxMVsfyNXrGNkNKL3Lw1xZ3JfaXyjUXsNN
----------------------------------------------------------------------

Quant à ta fausse impression qu'il conserve aussi les espaces, elle est
due au fait que la commande "echo" transforme en espaces les sauts de ligne.

----------------------------------------------------------------------
omiak$ echo $( sed -e 's/[^a-zA-Z0-9]//g' motus_clef )
bIRMcp0nQWnlyKOv03n5H7b6NY5I6iEiTiqcRS4ZFe33Fv1vTPiYGCV17vDUFvxDtnr5 H
dXaUdkLpne15mVJ0Kq0u01ZAjGlhS3Chl371vElQXoW7voI50fDb58GbiPxTmBybh u 7v
xVSCtpXM5SfgEZGFZW8 eZNREt3p3B7W5ko4IEqnqb03ib8c0s ICmkhAChG4Wg3wZYMxM
VsfyNXrGNkNKL3Lw1xZ3JfaXyjUXsNN
----------------------------------------------------------------------

(debian Sarge,GNU sed version 4.1.2)


(Cygwin, GNU sed version 4.1.5)

Avatar
Olivier Miakinen
Le 07/05/2006 20:57, je répondais à Thomas Harding :

Je ne sais pas s'est vrai en PHP, mais avec sed, a-zA-Z autorise aussi
les caractères accentués ET (curieusement) l'espace:


Je n'ai *jamais* vu ça sur aucun Unix.

[...]

(debian Sarge,GNU sed version 4.1.2)



La seule explication que je peux voir à un tel comportement, ce serait
que les fichiers sur ton système soient stockés sous un encodage multi
octets, le deuxième octet (ou les suivants) pouvant faire partie de la
table ASCII ; par exemple UTF-7, mais pas Latin-1 ni UTF-8. La commande
sed n'étant probablement pas prévue pour un jeu multi octets, elle ne
saurait pas distinguer les caractères ASCII de certains encodages.

Comme nous sommes de plus en plus hors charte dans ce groupe et que
je ne fréquente aucun groupe spécifiquement Unix, je te propose de
continuer la discussion en privé, et je positionne donc le suivi en
conséquence.

Attention : ta boîte est pleine, alors il faudra la vider avant de
pouvoir recevoir du courrier.

[ suivi positionné vers ma bàl ]

--
Olivier Miakinen


Avatar
Vincent Lascaux
Quant à strcspn, c'est toujours plus lent (10 à 20%).


Là encore, rien de surprenant, puisque l'on remplace un test du genre
« si >= 'a' et <= 'z' » par un du genre « si == 'a' ou == 'b' ou == 'c'
ou == 'd' ou ... ou == 'y' ou == 'z' ».


J'allais répondre que les codeurs de PHP n'était pas bêtes et qu'ils
utiliseraient un tableau pour ne pas avoir à refaire le test à chaque
caractere.
Mais pour en avoir le coeur net, et pour éviter de dire une connerie, je
suis allé verifier dans le code de PHP et voilà ce qu'on peut y voir:

PHPAPI size_t php_strcspn(char *s1, char *s2, char *s1_end, char *s2_end)
{
register const char *p, *spanp;
register char c = *s1;

for (p = s1;;) {
spanp = s2;
do {
if (*spanp == c || p == s1_end) {
return p - s1;
}
} while (spanp++ < s2_end);
c = *++p;
}
/* NOTREACHED */
}

Mais pourquoi le monde est il aussi cruel ?
Pourquoi le code n'est pas :

PHPAPI size_t php_strcspn(char *s1, char *s2, char *s1_end, char *s2_end)
{
bool isCharacterValid[256];
char* p;

memset(isCharacterValid, 0, sizeof(isCharacterValid));
for (p = s1; p < s1_end; p++)
isCharacterValid[*p] = true;

for (p = s2; p < s2_end && isCharacterValid[*p]; p++)
{ }

return p - s2;
}

--
Vincent


Avatar
Olivier Miakinen

J'allais répondre que les codeurs de PHP n'était pas bêtes et qu'ils
utiliseraient un tableau pour ne pas avoir à refaire le test à chaque
caractere.
[...]

Mais pourquoi le monde est il aussi cruel ?
Pourquoi le code n'est pas :

[...]

memset(isCharacterValid, 0, sizeof(isCharacterValid));
for (p = s1; p < s1_end; p++)
isCharacterValid[*p] = true;

[...]


Quitte à économiser des bouts de chandelle, il faudrait voir si ce code
est vraiment plus efficace dans la majorité des cas utilisés. J'imagine
que le plus souvent la chaîne s1 doit être assez petite, or ton memset
effectue une boucle avec 256 écritures en mémoire et 256 tests de fin
de boucle : cela peut être plus long que de passer en revue le ou les
caractères de s1 pour chaque caractère de s2 -- et ce encore plus si
le test de présence réussit dès le premier caractère de s2 !

1 2