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

161

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

Existe-t-il une fonction ou un morceau de code pour le faire efficacement?

Stewart Robinson
la source

Réponses:

355

ASCII 7 bits?

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

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

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

ASCII étendu 8 bits?

Vous êtes tombé dans une machine à voyager dans le temps 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 dans la plage 128-255. Un réglage facile - il suffit de rechercher 0-31 et 127

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

UTF-8?

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

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

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

Si vous avez affaire à Unicode, il existe 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 /umodificateur en place, vous pouvez simplement ajouter \xA0à la classe de caractères:

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

Addendum: qu'en est-il de str_replace?

preg_replace est assez efficace, mais si vous faites beaucoup cette opération, 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 être rapide, mais ce n'est pas toujours le cas, vous devez absolument vous comparer pour voir si cela vous permet d'économiser quelque chose. J'ai fait quelques tests 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 durées elles-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 toujours preg_replace gagner. Dans la plage de 1 à 8 Ko, str_replace avait un bord marginal.

J'ai trouvé que c'était un résultat intéressant, alors incluez-le 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 le comparer à vos propres données, puis de décider.

Paul Dixon
la source
14
Si vous avez besoin de considérer une nouvelle ligne sûre, changez l'expression en ceci (recherche inversement des imprimables): preg_replace (/ [^ \ x0A \ x20- \ x7E] /, '', $ string);
Nick
12
@Dalin Il n'existe pas de «caractère UTF-8». Il existe des symboles / caractères Unicode et UTF-8 est un encodage qui peut tous les représenter. Vous vouliez dire que cela ne fonctionne pas pour les caractères en dehors du jeu de caractères ASCII.
Mathias Bynens
3
Si vous devez faire correspondre un caractère Unicode au-dessus de \ xFF, utilisez \ x {####}
Peter Olson
vous avez manqué \ x7F (127) qui est un caractère non imprimable
Mubashar
cela supprimera les lettres arabes, mauvaise solution.
Ayman Hussein
141

La plupart des autres réponses ici 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 y a une classe étrange de caractères dans la plage \x80-\x9F(juste au-dessus de la plage de caractères ASCII 7 bits) qui sont techniquement des caractères de contrôle, mais avec le temps, elles ont été mal utilisées pour des caractères imprimables. Si vous n'avez aucun problème avec ceux-ci, 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 chariot, les tabulations, les espaces insécables et les tirets souples, 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 supprimer sauf les caractères ASCII imprimables 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

Dalin
la source
1
Votre expression régulière gère bien les caractères UTF8; mais il supprime les caractères "spéciaux" non UTF8; comme ç, ü et ö. '/[\x00-\x1F\x80-\xC0]/u'les laisse intacts; mais aussi le signe de division (F7) et de multiplication (D7).
Hazar
@Hazar oui vous avez raison \ x80- \ xFF trop dépouillé, mais \ x80- \ xC0 est encore trop restrictif. Cela manquerait d'autres caractères imprimables comme © £ ±. Pour référence, voir utf8-chartable.de
Dalin
1
@TimMalone parce que PHP étendra ces séquences de caractères: php.net/manual/en/ ... ainsi l'expression régulière ne verra pas la plage dont vous essayez de lui parler.
Dalin
1
Et 7F? Cela ne devrait-il pas être le cas \x7F-\x9F?
Bell
1
J'ai juste essayé beaucoup, j'ai essayé toutes les fonctions d'encodage disponibles en PHP de regex à mb_ en passant par htmlspecialchars, etc. Rien n'a supprimé les caractères de contrôle, merci d'avoir investi le travail.
John
29

À partir de PHP 5.2, nous avons également accès à filter_var, dont je n'ai vu aucune mention, alors j'ai pensé que je le jetterais là-bas. Pour utiliser filter_var pour supprimer les caractères non imprimables <32 et> 127, vous pouvez faire:

Filtrez les caractères ASCII inférieurs à 32

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

Filtrer les caractères ASCII au-dessus de 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 décapant haut:

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

Il existe également des options pour supprimer le HTML, nettoyer les e-mails et les URL, etc. Donc, de nombreuses options pour le nettoyage (supprimer les données) et même la validation (retourner false si non valide plutôt que supprimer silencieusement).

Désinfection: http://php.net/manual/en/filter.filters.sanitize.php

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

Cependant, il y a toujours le problème, que le FILTER_FLAG_STRIP_LOW supprime les retours à la ligne et les retours chariot, qui pour une zone de texte sont des caractères complètement valides ... donc certaines des réponses Regex, je suppose, sont encore nécessaires parfois, par exemple après avoir examiné ceci thread, je prévois de le faire pour les zones de texte:

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

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

Kevin Nelson
la source
27

vous pouvez utiliser des classes de caractères

/[[:cntrl:]]+/
ghostdog74
la source
cela ne m'oblige-t-il pas à utiliser ereg?
Stewart Robinson
18

c'est plus simple:

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

jacktrade
la source
5
Cela supprime également les sauts de ligne, les retours chariot et les caractères UTF8.
Dalin
5
@Dalin Il n'existe pas de «caractère UTF-8». Il existe des symboles / caractères Unicode et UTF-8 est un encodage qui peut tous les représenter. Vous vouliez dire que cela supprime également les caractères en dehors de la plage ASCII .
Mathias Bynens
1
Mange des caractères arabes :)
Rolf
16

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) était conforme à utf8, mais avait plusieurs mauvaises séquences. Je suppose que la plupart d'entre eux étaient du contrôle ou du 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, il y a la table contre le serveur contre la connexion contre le rendu du contenu, comme on en parle un peu ici

Wayne Weibel
la source
1
Le seul qui passe tous mes tests unitaires, génial!
Korri
\ xE2 \ x80 [\ xA4- \ xA8] (ou 226.128. [164-168]) - est faux, la séquence comprend les symboles imprimables suivants: caractère Unicode 'ONE DOT LEADER' (U + 2024), caractère Unicode 'TWO DOT LEADER '(U + 2025), caractère Unicode' HORIZONTAL ELLIPSIS '(U + 2026), caractère Unicode' HYPHENATION POINT '(U + 2027). Et un seul non imprimable: le caractère Unicode 'LINE SEPARATOR' (U + 2028). Le suivant est également non imprimable: Caractère Unicode 'PARAGRAPH SEPARATOR' (U + 2029). Remplacez donc la séquence par: \ xE2 \ x80 [\ xA8- \ xA9] \ xE2 \ x80 [\ xA8- \ xA9] pour supprimer LINE SEPARATOR et PARAGRAPH SEPARATOR.
MingalevME
C'est la meilleure solution que j'ai pu trouver jusqu'à présent, mais j'ai dû ajouter $s = preg_replace('/(\xF0\x9F[\x00-\xFF][\x00-\xFF])/', ' ', $s);parce que tous les personnages emoji gâchaient mysql
Joe Black
10

Ma version compatible UTF-8:

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

cédivad
la source
7
Cela supprime bien les caractères comme les guillemets, les crochets, etc. Ce sont certainement des caractères imprimables.
Gajus
c'est merveilleux! cela m'a sauvé la vie, a foiré en imprimant des caractères arabes, a fonctionné comme un champion :)
krishna
6

Vous pouvez utiliser un express régulier pour tout supprimer à l'exception des caractères que vous souhaitez conserver:

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

Remplace tout ce qui n'est pas (^) les lettres AZ ou az, les chiffres 0-9, l'espace, le trait de soulignement, l'hypen, le plus et l'esperluette - par rien (c'est-à-dire supprimez-le).

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

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

Gajus
la source
1
Cela fonctionne parfaitement pour moi! J'ai ajouté juste /upour les caractères UTF-8. Pouvez-vous expliquer ce que fait la première partie (?!\n)?
Marcio Mazzucato
4

Pour supprimer tous les caractères non ASCII de la chaîne d'entrée

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

Ce code supprime tous les caractères dans les plages hexadécimales 0-31 et 128-255, ne laissant que les caractères hexadécimaux 32-127 dans la chaîne résultante, que j'appelle $ result dans cet exemple.

Junaid Masood
la source
3

La réponse de @PaulDixon est complètement fausse , car elle supprime les caractères ASCII étendus imprimables 128-255!a été partiellement corrigé. Je ne sais pas pourquoi il veut toujours supprimer 128-255 d'un ensemble ASCII 7 bits à 127 caractères car il n'a pas les caractères ASCII étendus.

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

Et cela tuera 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 multi-octets). Alors ne fais pas ça! Ce sont des caractères entièrement légaux dans tous les systèmes de fichiers actuellement utilisés. La seule plage réservée est 0-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 en ASCII et UTF-8 car les deux partagent la même plage de jeu de contrôle .

L' alternative la plus lente et la plus rapide 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 voulez garder tous les caractères blancs \t, \net \r, puis retirer chr(9), chr(10)et chr(13)de cette liste. Remarque: L'espace blanc habituel est chr(32)qu'il reste dans le résultat. Décidez vous-même si vous souhaitez supprimer l'espace insécable chr(160)car cela peut causer des problèmes.

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

mgutt
la source
2

que diriez-vous:

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

me donne un contrôle total sur ce que je veux inclure

sdfor
la source
0

La réponse marquée est parfaite mais elle 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);
Mubashar
la source
Cette réponse est également fausse. Voir: stackoverflow.com/a/42058165/318765
mgutt
La réponse ci-dessus était un compliment à la réponse originale qui n'ajoute que le caractère «supprimer».
Mubashar
0

"cedivad" a résolu le problème pour moi avec le résultat persistant des chars suédois ÅÄÖ.

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

Merci!

Andreas Ek
la source
0

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

Appelez 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));
    }
?>
Faites le tomber comme si c'etait chaud
la source
0

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

use ForceUTF8\Encoding;

$string = Encoding::fixUTF8($string);
mnv
la source
1
Cette bibliothèque convertit les caractères accentués UTF-8 et les émoticônes UTF-8 en "?" symboles. Problème assez grave malheureusement.
ChristoKiwi
0

Le regex dans la réponse sélectionnée échoue pour Unicode: 0x1d (avec php 7.4)

une solution:

<?php
        $ct = 'différents'."\r\n test";

        // fail for Unicode: 0x1d
        $ct = preg_replace('/[\x00-\x1F\x7F]$/u', '',$ct);

        // work for Unicode: 0x1d
        $ct =  preg_replace( '/[^\P{C}]+/u', "",  $ct);

        // work for Unicode: 0x1d and allow line break
        $ct =  preg_replace( '/[^\P{C}\n]+/u', "",  $ct);

        echo $ct;

from: UTF 8 String supprime tous les caractères invisibles sauf le saut de ligne

Mkdgs
la source