it-swarm.dev

PHP: Como remover todos os caracteres não imprimíveis em uma string?

Eu imagino que preciso remover os caracteres 0-31 e 127,

Existe uma função ou um código para fazer isso de forma eficiente?.

137
Stewart Robinson

7 bits ASCII?

Se o seu Tardis acabou de chegar em 1963, e você quer apenas o 7 bit imprimível ASCII chars, você pode copiar tudo de 0 a 31 e 127 a 255 com isto:

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

Ele corresponde a qualquer coisa no intervalo de 0 a 31, 127 a 255 e remove-o. 

8 bits estendido ASCII?

Você caiu em um Time Machine Hot Tub, e você está de volta nos anos oitenta. Se você tem alguma forma de ASCII de 8 bits, então você pode querer manter os caracteres no intervalo de 128-255. Um ajuste fácil - basta olhar para 0-31 e 127

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

UTF-8?

Ah, bem vindo de volta ao século 21. Se você tem uma string codificada em UTF-8, então o /umodifier pode ser usado no regex

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

Isso apenas remove 0-31 e 127. Isso funciona em ASCII e UTF-8 porque ambos compartilham o mesmo intervalo de conjunto de controles (como observado por mgutt abaixo). Estritamente falando, isso funcionaria sem o modificador /u. Mas isso facilita a vida se você quiser remover outros caracteres ...

Se você está lidando com Unicode, existem potencialmente muitos elementos não-imprimíveis , mas vamos considerar um simples: NO-BREAK SPACE (U + 00A0)

Em uma string UTF-8, isso seria codificado como 0xC2A0. Você pode procurar e remover essa sequência específica, mas com o modificador /u no lugar, você pode simplesmente adicionar \xA0 à classe de caractere:

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

Adenda: E sobre str_replace?

preg_replace é bastante eficiente, mas se você está fazendo esta operação muito, você pode construir uma matriz de caracteres que você deseja remover, e usar str_replace como observado por mgutt abaixo, por exemplo.

//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, isso parece que seria rápido, mas nem sempre é o caso, você deve definitivamente comparar para ver se você economiza alguma coisa. Fiz alguns benchmarks em vários comprimentos de string com dados aleatórios, e esse padrão surgiu usando o 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

Os tempos próprios são para 10000 iterações, mas o que é mais interessante são as diferenças relativas. Até 512 chars, eu estava vendo preg_replace sempre vencer. No intervalo de 1-8kb, o str_replace tinha um Edge marginal. 

Eu pensei que era um resultado interessante, então incluí-lo aqui. O importante é não pegar este resultado e usá-lo para decidir qual método utilizar, mas para fazer benchmark com seus próprios dados e depois decidir.

292
Paul Dixon

Muitas das outras respostas aqui não levam em conta caracteres unicode (por exemplo, öäüßйȝîûηы ე மி ᚉ ⠛). Nesse caso, você pode usar o seguinte:

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

Há uma estranha classe de caracteres no intervalo \x80-\x9F (logo acima do intervalo de caracteres de 7 bits ASCII) que são tecnicamente caracteres de controle, mas com o tempo foram indevidamente utilizados para caracteres imprimíveis. Se você não tem nenhum problema com isso, então você pode usar:

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

Se você deseja também separar feeds de linha, retornos de carro, tabulações, espaços sem quebra e hífens, você pode usar:

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

Observe que você deve usar aspas simples para os exemplos acima.

Se você deseja remover tudo, exceto os caracteres básicos imprimíveis ASCII (todos os caracteres de exemplo acima serão removidos), você pode usar:

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

Para referência, consulte http://www.fileformat.info/info/charset/UTF-8/list.htm

132
Dalin

você pode usar classes de personagem

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

Começando com PHP 5.2, nós também temos acesso ao filter_var, o qual eu não vi nenhuma menção, então pensei em lançá-lo lá fora. Para usar o filter_var para remover caracteres não imprimíveis <32 e> 127, você pode fazer:

Filtrar caracteres ASCII abaixo de 32

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

Filtrar caracteres ASCII acima 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);

Você também pode codificar em html os caracteres baixos (nova linha, tabulação, etc.) durante a extração:

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

Há também opções para remover HTML, sanear e-mails e URLs, etc. Portanto, há muitas opções para sanitização (remover dados) e até mesmo validação (retorno falso se não for válido, em vez de remoção silenciosa).

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

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

No entanto, ainda existe o problema, que o FILTER_FLAG_STRIP_LOW irá remover os retornos de nova linha e carro, que para uma área de texto são caracteres completamente válidos ... então algumas respostas do Regex, eu acho, ainda são necessárias às vezes, por exemplo depois de revisar este tópico, planejo fazer isso para textareas:

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

Isso parece mais legível do que um número de regexes retirados por intervalo numérico.

23
Kevin Nelson

isso é mais simples:

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

18
jacktrade

Todas as soluções funcionam parcialmente, e mesmo abaixo provavelmente não cobre todos os casos. Meu problema estava em tentar inserir uma string em uma tabela mysql utf8. A string (e seus bytes) estavam todos em conformidade com utf8, mas tinham várias seqüências ruins. Eu suponho que a maioria deles foi controle ou formatação.

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 agravar ainda mais o problema é a tabela vs. servidor vs. conexão vs. renderização do conteúdo, como falei um pouco aqui

14
Wayne Weibel

Minha versão compatível com UTF-8:

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

9
cedivad

Você pode usar um expresso regular para remover tudo, exceto os caracteres que deseja manter:

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

Substitui tudo o que não é (^) as letras A-Z ou a-z, os números de 0 a 9, espaço, sublinhado, hífen, mais ee comercial - sem nada (ou seja, removê-lo).

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

Isto irá remover todos os caracteres de controle ( http://uk.php.net/manual/en/regexp.reference.unicode.php ) deixando os caracteres de nova linha \n. Pela minha experiência, os caracteres de controle são os que mais frequentemente causam os problemas de impressão.

5
Gajus

A resposta de @PaulDixon é completamente errado, porque remove o imprimívelextended ASCII caracteres 128-255! foi parcialmente corrigido. Não sei por que ele ainda deseja excluir 128-255 de um conjunto de 127 caracteres de 7 bits ASCII, pois ele não possui os caracteres estendidos ASCII. 

Mas finalmente foi importante não excluir 128-255 porque, por exemplo, chr(128) (\x80) é o sinal de euro em 8 bits ASCII e muitas fontes UTF-8 no Windows exibem um sinal de euro e Android meu próprio teste.

E isso matará muitos caracteres UTF-8 se você remover os caracteres ASCII 128-255 de uma string UTF-8 (provavelmente os bytes iniciais de um caractere UTF-8 de múltiplos bytes). Então não faça isso! Eles são caracteres completamente legais em todos os sistemas de arquivos usados ​​atualmente. O único intervalo reservado é 0-31 .

Em vez disso, use isso para excluir os caracteres não imprimíveis 0-31 e 127:

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

É funciona em ASCII e UTF-8 porque ambos compartilham o mesmo intervalo de conjunto de controles .

o mais rápido mais lenta¹ alternativa sem usar expressões 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);

Se você deseja manter todos os caracteres de espaço em branco \t, \n e \r, remova chr(9), chr(10) e chr(13) desta lista. Nota: O espaço em branco usual é chr(32), portanto permanece no resultado. Decida-se se você deseja remover chr(160) de espaço não-violador, pois isso pode causar problemas.

¹ Testado pelo @PaulDixon e verificado por mim mesmo.

3
mgutt

que tal:

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

me dá controle total do que eu quero incluir

2
sdfor

Resposta marcada é perfeita, mas falta o personagem 127 (DEL), que também é um personagem não imprimível

minha resposta seria 

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

Eu resolvi problema para UTF8 usando https://github.com/neitanod/forceutf8

use ForceUTF8\Encoding;

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

"cedivad" resolveu a questão para mim com resultado persistente de chars sueco ÅÄÖ.

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

Obrigado!

0
Andreas Ek

Para qualquer um que ainda esteja procurando como fazer isso sem remover os caracteres não imprimíveis, mas sim escapando deles, fiz isso para ajudar. Sinta-se livre para melhorá-lo! Os caracteres são escapados para \\ x [A-F0-9] [A-F0-9].

Ligue assim:

$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

Para remover todos os caracteres não-ASCII da cadeia de entrada

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

Esse código remove todos os caracteres nos intervalos hexadecimais de 0 a 31 e 128 a 255, deixando apenas os caracteres hexadecimais 32 a 127 na cadeia resultante, que chamo de $ result neste exemplo.

0
Junaid Masood