web-dev-qa-db-fra.com

PHP: Comment supprimer tous les caractères non imprimables d'une chaîne?

J'imagine que j'ai besoin de supprimer les caractères 0-31 et 127,

Y at-il une fonction ou un morceau de code pour le faire efficacement.

137
Stewart Robinson

ASCII 7 bits?

Si votre Tardis vient d’atterrir en 1963 et que vous voulez seulement les caractères ASCII imprimables 7 bits, vous pouvez tout extraire entre 0-31 et 127-255 avec ceci:

$string = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '', $string);

Il correspond à tout ce qui se situe dans la plage 0-31, 127-255 et le supprime.

ASCII étendu 8 bits?

Vous êtes tombé dans une machine à remonter le temps dans un bain à remous et vous êtes de retour dans les années quatre-vingt. Si vous avez une forme d'ASCII 8 bits, vous voudrez peut-être garder les caractères entre 128 et 255. Un réglage facile - cherchez 0-31 et 127

$string = preg_replace('/[\x00-\x1F\x7F]/', '', $string);

UTF-8?

Ah, bienvenue au 21ème siècle. Si vous avez une chaîne encodée en UTF-8, le /umodificateur peut être utilisé dans l'expression régulière.

$string = preg_replace('/[\x00-\x1F\x7F]/u', '', $string);

Cela supprime simplement 0-31 et 127. Cela fonctionne dans ASCII et UTF-8 car les deux partagent le même plage de jeu de contrôles (comme indiqué par mgutt ci-dessous). Strictement parlant, cela fonctionnerait sans le modificateur /u. Mais cela facilite la vie si vous souhaitez supprimer d'autres caractères ...

Si vous utilisez Unicode, il y a potentiellement de nombreux éléments non imprimables , mais considérons un élément simple: NO-BREAK SPACE (U + 00A0)

Dans une chaîne UTF-8, cela serait codé comme 0xC2A0. Vous pouvez rechercher et supprimer cette séquence spécifique, mais avec le modificateur /u en place, vous pouvez simplement ajouter \xA0 à la classe de caractères:

$string = preg_replace('/[\x00-\x1F\x7F\xA0]/u', '', $string);

Addendum: et str_replace?

preg_replace est assez efficace, mais si vous effectuez cette opération fréquemment, vous pouvez créer un tableau de caractères que vous souhaitez supprimer et utiliser str_replace comme indiqué par mgutt ci-dessous, par exemple.

//build an array we can re-use across several operations
$badchar=array(
    // control characters
    chr(0), chr(1), chr(2), chr(3), chr(4), chr(5), chr(6), chr(7), chr(8), chr(9), chr(10),
    chr(11), chr(12), chr(13), chr(14), chr(15), chr(16), chr(17), chr(18), chr(19), chr(20),
    chr(21), chr(22), chr(23), chr(24), chr(25), chr(26), chr(27), chr(28), chr(29), chr(30),
    chr(31),
    // non-printing characters
    chr(127)
);

//replace the unwanted chars
$str2 = str_replace($badchar, '', $str);

Intuitivement, cela semble rapide, mais ce n’est pas toujours le cas. Vous devez absolument évaluer si cela vous épargne quelque chose. J'ai fait quelques points de repère sur une variété de longueurs de chaîne avec des données aléatoires, et ce modèle est apparu en utilisant PHP 7.0.12

     2 chars str_replace     5.3439ms preg_replace     2.9919ms preg_replace is 44.01% faster
     4 chars str_replace     6.0701ms preg_replace     1.4119ms preg_replace is 76.74% faster
     8 chars str_replace     5.8119ms preg_replace     2.0721ms preg_replace is 64.35% faster
    16 chars str_replace     6.0401ms preg_replace     2.1980ms preg_replace is 63.61% faster
    32 chars str_replace     6.0320ms preg_replace     2.6770ms preg_replace is 55.62% faster
    64 chars str_replace     7.4198ms preg_replace     4.4160ms preg_replace is 40.48% faster
   128 chars str_replace    12.7239ms preg_replace     7.5412ms preg_replace is 40.73% faster
   256 chars str_replace    19.8820ms preg_replace    17.1330ms preg_replace is 13.83% faster
   512 chars str_replace    34.3399ms preg_replace    34.0221ms preg_replace is  0.93% faster
  1024 chars str_replace    57.1141ms preg_replace    67.0300ms str_replace  is 14.79% faster
  2048 chars str_replace    94.7111ms preg_replace   123.3189ms str_replace  is 23.20% faster
  4096 chars str_replace   227.7029ms preg_replace   258.3771ms str_replace  is 11.87% faster
  8192 chars str_replace   506.3410ms preg_replace   555.6269ms str_replace  is  8.87% faster
 16384 chars str_replace  1116.8811ms preg_replace  1098.0589ms preg_replace is  1.69% faster
 32768 chars str_replace  2299.3128ms preg_replace  2222.8632ms preg_replace is  3.32% faster

Les timings eux-mêmes sont pour 10000 itérations, mais ce qui est plus intéressant, ce sont les différences relatives. Jusqu'à 512 caractères, je voyais preg_replace toujours gagner. Str_replace avait un bord marginal dans la plage 1-8kb.

Je pensais que c'était un résultat intéressant, donc l'inclure ici. L'important n'est pas de prendre ce résultat et de l'utiliser pour décider de la méthode à utiliser, mais de vous baser sur vos propres données, puis de décider.

314
Paul Dixon

La plupart des autres réponses ne prennent pas en compte les caractères unicode (par exemple, öäüßйȝîûηы ე மி ᚉ). Dans ce cas, vous pouvez utiliser les éléments suivants:

$string = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]/u', '', $string);

Il existe une classe de caractères étrange dans la plage \x80-\x9F (juste au-dessus de la plage de caractères à 7 bits ASCII) qui sont techniquement des caractères de contrôle, mais au fil du temps, ils ont été mal utilisés pour les caractères imprimables. Si cela ne vous pose aucun problème, vous pouvez utiliser:

$string = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/u', '', $string);

Si vous souhaitez également supprimer les sauts de ligne, les retours à la ligne, les tabulations, les espaces insécables et les traits d'union, vous pouvez utiliser:

$string = preg_replace('/[\x00-\x1F\x7F-\xA0\xAD]/u', '', $string);

Notez que vous devez utiliser des guillemets simples pour les exemples ci-dessus.

Si vous souhaitez tout effacer sauf les caractères imprimables ASCII de base (tous les exemples de caractères ci-dessus seront supprimés), vous pouvez utiliser:

$string = preg_replace( '/[^[:print:]]/', '',$string);

Pour référence, voir http://www.fileformat.info/info/charset/UTF-8/list.htm

132
Dalin

vous pouvez utiliser des classes de caractères

/[[:cntrl:]]+/
25
ghostdog74

En commençant par PHP 5.2, nous avons également accès à filter_var, ce dont je n’ai jamais entendu parler, je pensais donc le jeter. Pour utiliser filter_var afin de supprimer les caractères non imprimables <32 et> 127, vous pouvez effectuer les opérations suivantes:

Filtre ASCII caractères inférieurs à 32

$string = filter_var($input, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW);

Filtre ASCII caractères supérieurs à 127

$string = filter_var($input, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_HIGH);

Dénudez les deux:

$string = filter_var($input, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW|FILTER_FLAG_STRIP_HIGH);

Vous pouvez également encoder en HTML des caractères bas (nouvelle ligne, tabulation, etc.) tout en supprimant les caractères hauts:

$string = filter_var($input, FILTER_UNSAFE_RAW, FILTER_FLAG_ENCODE_LOW|FILTER_FLAG_STRIP_HIGH);

Il existe également des options de suppression du code HTML, de nettoyage des e-mails et des URL, etc. Ainsi, de nombreuses options de nettoyage (suppression des données) et même de validation (renvoient false si non valide, plutôt que de suppression automatique).

Sanitization:http://php.net/manual/fr/filter.filters.sanitize.php

Validation:http://php.net/manual/en/filter.filters.validate.php

Cependant, le problème persiste: FILTER_FLAG_STRIP_LOW supprimera les retours à la ligne et les retours à la ligne, qui, pour une zone de texte, sont des caractères complètement valides ... certaines réponses Regex sont donc toujours nécessaires, par exemple Après avoir examiné ce fil, je prévois de le faire pour textareas:

$string = preg_replace( '/[^[:print:]\r\n]/', '',$input);

Cela semble plus lisible qu'un certain nombre de regex supprimées par plage numérique.

23
Kevin Nelson

c'est plus simple:

$ string = preg_replace ( '/ [^ [: commande:]] /', '', $ string);

18
jacktrade

Toutes les solutions fonctionnent partiellement et même ci-dessous ne couvre probablement pas tous les cas. Mon problème était d'essayer d'insérer une chaîne dans une table mysql utf8. La chaîne (et ses octets) étaient tous conformes à utf8, mais comportaient plusieurs mauvaises séquences. Je suppose que la plupart d'entre eux étaient sous contrôle ou formatage.

function clean_string($string) {
  $s = trim($string);
  $s = iconv("UTF-8", "UTF-8//IGNORE", $s); // drop all non utf-8 characters

  // this is some bad utf-8 byte sequence that makes mysql complain - control and formatting i think
  $s = preg_replace('/(?>[\x00-\x1F]|\xC2[\x80-\x9F]|\xE2[\x80-\x8F]{2}|\xE2\x80[\xA4-\xA8]|\xE2\x81[\x9F-\xAF])/', ' ', $s);

  $s = preg_replace('/\s+/', ' ', $s); // reduce all multiple whitespace to a single space

  return $s;
}

Pour aggraver encore le problème, la table vs serveur vs connexion vs rendu du contenu, comme parle un peu ici _

14
Wayne Weibel

Ma version compatible UTF-8:

preg_replace('/[^\p{L}\s]/u','',$value);

9
cedivad

Vous pouvez utiliser un moyen régulier pour tout supprimer sauf les caractères que vous souhaitez conserver:

$string=preg_replace('/[^A-Za-z0-9 _\-\+\&]/','',$string);

Remplace tout ce qui n’est pas (^) les lettres A-Z ou a-z, les chiffres 0 à 9, espace, trait de soulignement, hypen, plus et esperluette - par rien (c’est-à-dire le supprimer).

6
Richy B.
preg_replace('/(?!\n)[\p{Cc}]/', '', $response);

Cela supprimera tous les caractères de contrôle ( http://uk.php.net/manual/fr/regexp.reference.unicode.php ) en laissant les caractères de retour à la ligne \n. D'après mon expérience, les caractères de contrôle sont ceux qui causent le plus souvent des problèmes d'impression.

5
Gajus

Le réponse de @PaulDixon est complètement faux, parce que supprime les caractères imprimables _ ​​ caractères étendus ASCII } _ 128-255! a été partiellement corrigé. Je ne sais pas pourquoi il veut toujours supprimer 128-255 d'un jeu ASCII 7 bits à 127 caractères car il ne comporte pas les caractères étendus ASCII. 

Mais finalement, il était important de ne pas supprimer 128-255, car par exemple chr(128) (\x80) est le signe de l'euro dans 8 bits ASCII et de nombreuses polices UTF-8 dans Windows afficher un signe de l'euro Android concernant mon propre test.

Et cela supprimera de nombreux caractères UTF-8 si vous supprimez les caractères ASCII 128-255 d'une chaîne UTF-8 (probablement les octets de départ d'un caractère UTF-8 à plusieurs octets). Alors ne fais pas ça! Ce sont des caractères complètement légaux dans tous les systèmes de fichiers actuellement utilisés. _ { La seule plage réservée est comprise entre 0 et 31 .

Utilisez plutôt ceci pour supprimer les caractères non imprimables 0-31 et 127:

$string = preg_replace('/[\x00-\x1F\x7F]/', '', $string);

Il fonctionne dans ASCII et UTF-8 car ils partagent le même { même plage de jeux de contrôles }.

Le le plus rapide alternative plus lente¹ sans utiliser d'expressions régulières:

$string = str_replace(array(
    // control characters
    chr(0), chr(1), chr(2), chr(3), chr(4), chr(5), chr(6), chr(7), chr(8), chr(9), chr(10),
    chr(11), chr(12), chr(13), chr(14), chr(15), chr(16), chr(17), chr(18), chr(19), chr(20),
    chr(21), chr(22), chr(23), chr(24), chr(25), chr(26), chr(27), chr(28), chr(29), chr(30),
    chr(31),
    // non-printing characters
    chr(127)
), '', $string);

Si vous souhaitez conserver tous les espaces blancs \t, \n et \r, supprimez chr(9), chr(10) et chr(13) de cette liste. Remarque: L'espace habituel est chr(32) et reste donc dans le résultat. Décidez vous-même si vous souhaitez supprimer l’espace insécable chr(160) car cela peut poser des problèmes.

¹ Testé par @PaulDixon et vérifié par moi-même.

3
mgutt

que diriez-vous:

return preg_replace("/[^a-zA-Z0-9`_.,;@#%~'\"\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:\-\s\\\\]+/", "", $data);

me donne le contrôle complet de ce que je veux inclure

2
sdfor

La réponse marquée est parfaite mais il manque le caractère 127 (DEL) qui est également un caractère non imprimable.

ma réponse serait 

$string = preg_replace('/[\x00-\x1F\x7f-\xFF]/', '', $string);
1
Mubashar Ahmad

J'ai résolu le problème pour UTF8 avec https://github.com/neitanod/forceutf8

use ForceUTF8\Encoding;

$string = Encoding::fixUTF8($string);
0
mnv

"cedivad" m'a résolu le problème avec le résultat persistant des caractères suédois ÅÄÖ.

$text = preg_replace( '/[^\p{L}\s]/u', '', $text );

Merci!

0
Andreas Ek

Pour tous ceux qui cherchent encore comment faire cela sans supprimer les caractères non imprimables, mais plutôt leur échapper, j'ai conçu ceci pour aider. N'hésitez pas à l'améliorer! Les caractères sont échappés dans \\ x [A-F0-9] [A-F0-9].

Appelle comme ça:

$escaped = EscapeNonASCII($string);

$unescaped = UnescapeNonASCII($string);

<?php 
  function EscapeNonASCII($string) //Convert string to hex, replace non-printable chars with escaped hex
    {
        $hexbytes = strtoupper(bin2hex($string));
        $i = 0;
        while ($i < strlen($hexbytes))
        {
            $hexpair = substr($hexbytes, $i, 2);
            $decimal = hexdec($hexpair);
            if ($decimal < 32 || $decimal > 126)
            {
                $top = substr($hexbytes, 0, $i);
                $escaped = EscapeHex($hexpair);
                $bottom = substr($hexbytes, $i + 2);
                $hexbytes = $top . $escaped . $bottom;
                $i += 8;
            }
            $i += 2;
        }
        $string = hex2bin($hexbytes);
        return $string;
    }
    function EscapeHex($string) //Helper function for EscapeNonASCII()
    {
        $x = "5C5C78"; //\x
        $topnibble = bin2hex($string[0]); //Convert top nibble to hex
        $bottomnibble = bin2hex($string[1]); //Convert bottom nibble to hex
        $escaped = $x . $topnibble . $bottomnibble; //Concatenate escape sequence "\x" with top and bottom nibble
        return $escaped;
    }

    function UnescapeNonASCII($string) //Convert string to hex, replace escaped hex with actual hex.
    {
        $stringtohex = bin2hex($string);
        $stringtohex = preg_replace_callback('/5c5c78([a-fA-F0-9]{4})/', function ($m) { 
            return hex2bin($m[1]);
        }, $stringtohex);
        return hex2bin(strtoupper($stringtohex));
    }
?>
0
DropItLikeItsHot