PHP: ¿Cómo eliminar todos los caracteres no imprimibles en una cadena?


Imagino que necesito eliminar los caracteres 0-31 y 127,

Hay una función o pieza de código para hacer esto eficientemente.

Author: Stewart Robinson, 2009-07-24

16 answers

ASCII de 7 bits?

Si su Tardis acaba de aterrizar en 1963, y solo desea los caracteres ASCII imprimibles de 7 bits, puede extraer todo de 0-31 y 127-255 con esto:

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

Coincide con cualquier cosa en el rango 0-31, 127-255 y lo elimina.

¿ASCII extendido de 8 bits?

Te caíste en una Máquina del Tiempo de Bañera de hidromasaje, y estás de vuelta en los años ochenta. Si tiene alguna forma de ASCII de 8 bits, entonces es posible que desee mantener los caracteres en el rango 128-255. Un ajuste fácil - basta con buscar 0-31 y 127

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

UTF-8?

Ah, bienvenido de nuevo al siglo 21. Si tiene una cadena codificada en UTF-8, entonces el /u modificador se puede utilizar en la expresión regular

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

Esto solo elimina 0-31 y 127. Esto funciona en ASCII y UTF-8 porque ambos comparten el mismo rango de control (como señala mgutt a continuación). Estrictamente hablando, esto funcionaría sin el modificador /u. Pero hace la vida más fácil si desea eliminar otros chars...

Si se trata de Unicode, hay potencialmente muchos elementos no imprimibles, pero consideremos uno simple: ESPACIO SIN INTERRUPCIÓN (U+00A0)

En una cadena UTF-8, esto se codificaría como 0xC2A0. Puede buscar y eliminar esa secuencia específica, pero con el modificador /u en su lugar, simplemente puede agregar \xA0 a la clase de caracteres:

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

Anexo: ¿Qué pasa con str_replace?

Preg_replace es bastante eficiente, pero si usted está haciendo esta operación mucho, usted podría construir una matriz de caracteres que desea eliminar, y utilizar str_replace como se indica por mgutt a continuación, por ejemplo,

//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);

Intuitivamente, esto parece que sería rápido, pero no siempre es el caso, definitivamente debe comparar para ver si le ahorra algo. Hice algunos puntos de referencia a través de una variedad de longitudes de cadena con datos aleatorios, y este patrón surgió utilizando 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

Los tiempos en sí son para 10000 iteraciones, pero más interesantes son las diferencias relativas. Hasta 512 caracteres, estaba viendo preg_replace siempre ganar. En el rango de 1-8kb, str_replace tenía un borde marginal.

Pensé que era un resultado interesante, así que incluirlo aquí. Lo importante no es tomar este resultado y usarlo para decidir qué método usar, sino comparar con sus propios datos y luego decidir.

 280
Author: Paul Dixon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:55:03

Muchas de las otras respuestas aquí no tienen en cuenta los caracteres unicode (por ejemplo, öäüßйîîûηы)). En este caso se puede utilizar lo siguiente:

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

Hay una extraña clase de caracteres en el rango \x80-\x9F (justo por encima del rango ASCII de caracteres de 7 bits) que técnicamente son caracteres de control, pero con el tiempo se han utilizado incorrectamente para caracteres imprimibles. Si usted no tiene ningún problema con estos, entonces usted puede utilizar:

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

Si desea también pelar las alimentaciones de línea, carriage retornos, tabulaciones, espacios que no se rompen y guiones suaves, puede usar:

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

Tenga en cuenta que debe usar comillas simples para los ejemplos anteriores.

Si desea eliminar todo excepto los caracteres ASCII imprimibles básicos (se eliminarán todos los caracteres de ejemplo anteriores), puede usar:

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

Para referencia véase http://www.fileformat.info/info/charset/UTF-8/list.htm

 130
Author: Dalin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-07 14:40:53

Puede usar clases de caracteres

/[[:cntrl:]]+/
 24
Author: ghostdog74,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-24 10:57:16

Comenzando con PHP 5.2, también tenemos acceso a filter_var, del cual no he visto ninguna mención, así que pensé que lo lanzaría por ahí. Para usar filter_var para eliminar caracteres no imprimibles 127, puede hacer:

Filtrar caracteres ASCII por debajo de 32

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

Filtrar caracteres ASCII por encima de 127

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

Tira ambos:

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

También puede codificar caracteres bajos en html (nueva línea, pestaña, etc.) mientras que la extracción de alta:

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

También Hay opciones para stripping HTML, sanitizing e-mails y URLs, etc. Por lo tanto, muchas opciones para la desinfección (eliminar datos) e incluso la validación (devolver false si no es válido en lugar de eliminar silenciosamente).

Sanitización: http://php.net/manual/en/filter.filters.sanitize.php

Validación: http://php.net/manual/en/filter.filters.validate.php

Sin embargo, todavía existe el problema, que el FILTER_FLAG_STRIP_LOW eliminará la nueva línea y retornos de carro, que para un área de texto son completamente válidos characters...so algunas de las respuestas de expresiones regulares, supongo, todavía son necesarias a veces, por ejemplo, después de revisar este hilo, planeo hacer esto para textareas:

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

Esto parece más legible que un número de expresiones regulares que se eliminan por rango numérico.

 21
Author: Kevin Nelson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-10 18:27:36

Esto es más simple:

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

 16
Author: jipipayo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-23 09:14:50

Todas las soluciones funcionan parcialmente, e incluso abajo probablemente no cubre todos los casos. Mi problema fue intentar insertar una cadena en una tabla mysql utf8. La cadena (y sus bytes) se conformaban a utf8, pero tenían varias secuencias malas. Asumo que la mayoría de ellos eran control o formateo.

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;
}

Para exacerbar aún más el problema es la tabla vs. servidor vs. conexión vs. renderización del contenido, como habló un poco aquí

 13
Author: Wayne Weibel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:31

Mi versión compatible con UTF-8:

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

 9
Author: cedivad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-06 12:56:42

Puedes usar un express regular para eliminar todo excepto aquellos caracteres que deseas conservar:

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

Reemplaza todo lo que no es (^) las letras A-Z o a-z, los números 0-9, el espacio, el guion bajo, la exageración, el plus y el ampersand - con nada (es decir, eliminarlo).

 6
Author: Richy B.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-24 10:50:59
preg_replace('/(?!\n)[\p{Cc}]/', '', $response);

Esto eliminará todos los caracteres de control ( http://uk.php.net/manual/en/regexp.reference.unicode.php ) dejando los caracteres de nueva línea \n. Desde mi experiencia, los caracteres de control son los que más a menudo causan los problemas de impresión.

 4
Author: Gajus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-01 11:06:52

La respuesta de @ PaulDixon es completamente incorrecto , porque elimina el imprimible caracteres ASCII extendidos 128-255! se ha corregido parcialmente. No se por qué todavía quiere eliminar 128-255 de un conjunto ASCII de 7 bits de 127 caracteres, ya que no tiene los caracteres ASCII extendidos.

Pero finalmente era importante no eliminar 128-255 porque por ejemplo chr(128) (\x80) es el signo del euro en ASCII de 8 bits y muchas fuentes UTF-8 en Windows mostrar un signo de euro y Android con respecto a mi propia prueba.

Y matará muchos caracteres UTF-8 si elimina los caracteres ASCII 128-255 de una cadena UTF-8 (probablemente los bytes iniciales de un carácter UTF-8 de varios bytes). Así que no hagas eso! Son caracteres completamente legales en todos los sistemas de archivos utilizados actualmente. El único rango reservado es 0-31.

En su lugar, use esto para eliminar los caracteres no imprimibles 0-31 y 127:

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

It funciona en ASCII y UTF-8porque ambos comparten el mismo rango de control .

La alternativa más rápida más lenta 1 sin usar expresiones regulares:

$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 desea mantener todos los caracteres de espacio en blanco\t, \n y \r, luego eliminar chr(9), chr(10) y chr(13) de esta lista. Nota: El espacio en blanco habitual es chr(32) por lo que permanece en el resultado. Decida usted mismo si desea eliminar non-breaking space chr(160) ya que puede causar problemas.

¹ Probado por @PaulDixon y verificado por mí mismo.

 3
Author: mgutt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:43

Qué tal:

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

Me da un control completo de lo que quiero incluir

 2
Author: sdfor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-08 18:17:39

Marcado anwser es perfecto, pero falta el carácter 127(DEL), que también es un carácter no imprimible

Mi respuesta sería

$string = preg_replace('/[\x00-\x1F\x7f-\xFF]/', '', $string);
 1
Author: Mubashar Ahmad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-08 03:54:49

"cedivad" resolvió el problema para mí con el resultado persistente de los caracteres suecos ÅÄÖ.

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

Gracias!

 0
Author: Andreas Ek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-14 12:07:11

Para cualquiera que todavía esté buscando cómo hacer esto sin eliminar los caracteres no imprimibles, sino más bien escaparlos, hice esto para ayudar. Siéntase libre de mejorarlo! Los caracteres se escapan a \ \ x[A-F0-9] [A-F0-9].

Llama así:

$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
Author: DropItLikeItsHot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-28 18:22:14

Resolví el problema para UTF8 usando https://github.com/neitanod/forceutf8

use ForceUTF8\Encoding;

$string = Encoding::fixUTF8($string);
 0
Author: mnv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-03 08:55:51

Esto funcionó para mí. Tuve que convertir una cadena de cualquier tipo que era un título aleatorio en un slug para SEO.

function string2Slug($str){

    $str = trim($str);
    $str = str_replace(" ","_",$str);
    $temp = explode("\\u",$str);
    $str = '';
    foreach ($temp as $bit) {
        $str .= substr($bit,4);
    }

    $str = str_replace("'","",$str);
    $str = str_replace("\"","",$str);
    $str = str_replace("\\","",$str);
    $str = str_replace("\/","",$str);
    $str = str_replace("/","",$str);
    $str = str_replace("?","",$str);
    $str = str_replace("#","",$str);
    $str = str_replace("&","",$str);
    $str = str_replace("%","",$str);
    $str = str_replace("!","",$str);

    return $str;

}
 -1
Author: Jy Smt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-07 15:27:48