web-dev-qa-db-fra.com

Taille multi-octets en PHP?

Apparemment, il n'y a pas de mb_trim dans la famille mb_* , donc j'essaie d'en implémenter un pour moi.

J'ai récemment trouvé cette expression rationnelle dans un commentaire dans php.net :

/(^\s+)|(\s+$)/u

Donc, je le mettrais en œuvre de la manière suivante:

function multibyte_trim($str)
{
    if (!function_exists("mb_trim") || !extension_loaded("mbstring")) {
        return preg_replace("/(^\s+)|(\s+$)/u", "", $str);
    } else {
        return mb_trim($str);
    }
}

La regex me semble correcte, mais je suis extrêmement noob avec des expressions régulières. Cela supprimera-t-il efficacement tout espace Unicode au début/à la fin d'une chaîne?

32
federicot

Je ne sais pas ce que vous essayez de faire avec cette fonction récursive sans fin que vous définissez, mais si vous voulez juste un découpage respectant plusieurs octets, cela fonctionnera.

function mb_trim($str) {
  return preg_replace("/(^\s+)|(\s+$)/us", "", $str); 
}
18
kba

Cette version supporte le second paramètre optionnel $ charlist:

function mb_trim ($string, $charlist = null) 
{   
    if (is_null($charlist)) {
        return trim ($string);
    } else {
        $charlist = str_replace ('/', '\/', preg_quote ($charlist));
        return preg_replace ("/(^[$charlist]+)|([$charlist]+$)/us", '', $string);
    }
}

Ne supporte pas ".." pour les gammes cependant.

5
Edson Medina

Ok, j'ai donc pris la solution de @ edson-medina, corrigé un bug et ajouté des tests unitaires. Voici les 3 fonctions que nous utilisons pour donner des contreparties mb à trim, rtrim et ltrim.

////////////////////////////////////////////////////////////////////////////////////
//Add some multibyte core functions not in PHP
////////////////////////////////////////////////////////////////////////////////////
function mb_trim($string, $charlist = null) {
    if (is_null($charlist)) {
        return trim($string);
    } else {
        $charlist = preg_quote($charlist, '/');
        return preg_replace("/(^[$charlist]+)|([$charlist]+$)/us", '', $string);
    }
}
function mb_rtrim($string, $charlist = null) {
    if (is_null($charlist)) {
        return rtrim($string);
    } else {
        $charlist = preg_quote($charlist, '/');
        return preg_replace("/([$charlist]+$)/us", '', $string);
    }
}
function mb_ltrim($string, $charlist = null) {
    if (is_null($charlist)) {
        return ltrim($string);
    } else {
        $charlist = preg_quote($charlist, '/');
        return preg_replace("/(^[$charlist]+)/us", '', $string);
    }
}
////////////////////////////////////////////////////////////////////////////////////

Voici les tests unitaires que j'ai écrits pour les personnes intéressées:

public function test_trim() {
    $this->assertEquals(trim(' foo '), mb_trim(' foo '));
    $this->assertEquals(trim(' foo ', ' o'), mb_trim(' foo ', ' o'));
    $this->assertEquals('foo', mb_trim(' Åfooホ ', ' Åホ'));
}

public function test_rtrim() {
    $this->assertEquals(rtrim(' foo '), mb_rtrim(' foo '));
    $this->assertEquals(rtrim(' foo ', ' o'), mb_rtrim(' foo ', ' o'));
    $this->assertEquals('foo', mb_rtrim('fooホ ', ' ホ'));
}

public function test_ltrim() {
    $this->assertEquals(ltrim(' foo '), mb_ltrim(' foo '));
    $this->assertEquals(ltrim(' foo ', ' o'), mb_ltrim(' foo ', ' o'));
    $this->assertEquals('foo', mb_ltrim(' Åfoo', ' Å'));
}
5
Michael Taggart

Vous pouvez également couper des espaces non compatibles Ascii (espaces insécables par exemple) sur les chaînes UTF-8 avec preg_replace('/^\p{Z}+|\p{Z}+$/u','',$str);.

\s ne fera correspondre que le caractère espace "compatible ascii" même avec le modificateur u.
mais \p{Z} correspondra à tous les caractères d'espace unicode connus

4
Opty

mb_ereg_replace semble contourner cela:

function mb_trim($str,$regex = "(^\s+)|(\s+$)/us") {
    return mb_ereg_replace($regex, "", $str);
}

..mais je ne connais pas suffisamment les expressions régulières pour savoir comment vous ajouteriez ensuite le paramètre "charlist" que les gens s'attendraient à pouvoir alimenter avec trim () - c'est-à-dire une liste de caractères à couper - alors venez fait de la regex un paramètre.

Il se peut que vous ayez un tableau de caractères spéciaux, puis parcourez chaque caractère de la liste de caractères et échappez-les en conséquence lors de la création de la chaîne de regex.

2
trapper_hag

Mes deux centimes

La solution réelle à votre question est que vous devez d’abord effectuer des vérifications d’encodage avant de modifier des chaînes d’entrée étrangères. Beaucoup apprennent vite à «désinfecter et valider» les données d'entrée, mais tardent à identifier l'étape d'identification de la nature sous-jacente (codage de caractères) des chaînes avec lesquelles ils travaillent.

Combien d'octets seront utilisés pour représenter chaque caractère? Avec le format UTF-8 correctement formaté, il peut être 1 (les caractères traités par trim), 2, 3 ou 4 octets. Le problème survient lorsque des représentations héritées, ou mal formées, de UTF-8 entrent en jeu - les limites des caractères d'octet peuvent ne pas s'aligner comme prévu (les profanes parlent).

En PHP, certains préconisent de forcer toutes les chaînes à se conformer au codage UTF-8 approprié (1, 2, 3 ou 4 octets par caractère), les fonctions telles que trim() fonctionnant toujours car la limite octets/caractères des caractères traite avec sera congruent pour les valeurs étendues ASCII/1 octet que trim() cherche à éliminer du début et de la fin d'une chaîne ( trim page de manuel ).

Cependant, étant donné que la programmation informatique est un domaine très diversifié, il est impossible d’avoir une approche globale qui fonctionne dans tous les scénarios. Cela dit, écrivez votre application comme il se doit pour fonctionner correctement. Vous venez de faire un site Web basé sur une base de données avec des entrées de formulaire? Oui, pour que mon argent force tout à être UTF-8.

Note: vous aurez toujours des problèmes d'internationalisation, même si votre problème UTF-8 est stable. Pourquoi? De nombreux jeux de caractères non anglais existent dans l'espace de 2, 3 ou 4 octets (points de code, etc.). Évidemment, si vous utilisez un ordinateur prenant en charge les scripts chinois, japonais, russe, arabe ou hébreu, vous souhaitez également que tout fonctionne avec 2, 3 et 4 octets! N'oubliez pas que la fonction PHP trim peut supprimer les caractères par défaut ou ceux spécifiés par l'utilisateur. Cela est important, surtout si vous avez besoin que votre trim prenne en compte certains caractères chinois. 

Je préférerais de loin traiter le problème de l’absence d’accès à mon site par une personne, puis le problème de l’accès et des réponses qui ne devraient pas se produire. Lorsque vous y réfléchissez, cela cadre avec les principes de moindre privilège (sécurité) et de conception universelle (accessibilité).

Résumé

Si les données d'entrée ne sont pas conformes au codage UTF-8 approprié, vous souhaiterez peut-être émettre une exception . Vous pouvez essayer d'utiliser les fonctions multi-octets PHP pour déterminer votre codage, ou une autre bibliothèque multi-octets. Si et quand PHP est écrit pour supporter pleinement l'unicode (Perl, Java ...), PHP s'en portera mieux. L'effort unicode PHP est mort il y a quelques années. Vous devez donc utiliser des bibliothèques supplémentaires pour gérer les chaînes UTF-8 à plusieurs octets. Ajouter simplement le drapeau /u à preg_replace() ne permet pas de voir la situation dans son ensemble.

Mettre à jour:

Cela dit, j’estime que le découpage multi-octets suivant serait utile pour ceux qui essaient d’extraire les ressources REST du composant de chemin d’url d’une URL chaîne de chemin.

function mb_path_trim($path)
{
    return preg_replace("/^(?:\/)|(?:\/)$/u", "", $path);
}
0
Anthony Rutledge