PHP: Come rimuovere tutti i caratteri non stampabili in una stringa?


161

Immagino di dover rimuovere i caratteri 0-31 e 127,

Esiste una funzione o un pezzo di codice per farlo in modo efficiente.

Risposte:


355

ASCII a 7 bit?

Se il tuo Tardis è appena atterrato nel 1963 e vuoi solo i caratteri ASCII stampabili a 7 bit, puoi strappare tutto da 0-31 e 127-255 con questo:

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

Corrisponde a qualsiasi cosa nell'intervallo 0-31, 127-255 e lo rimuove.

ASCII esteso a 8 bit?

Sei caduto in una macchina del tempo con vasca idromassaggio e sei tornato negli anni ottanta. Se hai una forma di ASCII a 8 bit, potresti voler mantenere i caratteri nell'intervallo 128-255. Una facile regolazione: basta cercare 0-31 e 127

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

UTF-8?

Ah, bentornato al 21 ° secolo. Se si dispone di una stringa codificata UTF-8, è possibile utilizzare il /u modificatore su regex

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

Questo rimuove solo 0-31 e 127. Funziona in ASCII e UTF-8 perché entrambi condividono lo stesso intervallo di set di controllo (come notato da mgutt di seguito). A rigor di termini, questo avrebbe funzionato senza il /umodificatore. Ma ti semplifica la vita se vuoi rimuovere altri caratteri ...

Se hai a che fare con Unicode, ci sono potenzialmente molti elementi non stampabili , ma consideriamo uno semplice: NO-BREAK SPACE (U + 00A0)

In una stringa UTF-8, questo sarebbe codificato come 0xC2A0. Puoi cercare e rimuovere quella specifica sequenza, ma con il /umodificatore in atto, puoi semplicemente aggiungere \xA0alla classe di caratteri:

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

Addendum: che dire di str_replace?

preg_replace è piuttosto efficiente, ma se stai facendo questa operazione molto, potresti creare una matrice di caratteri che vuoi rimuovere e usare str_replace come indicato da mgutt di seguito, ad es.

//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, sembra che sarebbe veloce, ma non è sempre così, dovresti assolutamente fare un benchmark per vedere se ti salva qualcosa. Ho fatto alcuni benchmark su diverse lunghezze di stringa con dati casuali e questo schema è emerso usando 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

I tempi stessi sono per 10000 iterazioni, ma ciò che è più interessante sono le differenze relative. Fino a 512 caratteri, vedevo sempre vincere preg_replace. Nell'intervallo 1-8kb, str_replace ha un margine marginale.

Ho pensato che fosse un risultato interessante, quindi includilo qui. L'importante è non prendere questo risultato e usarlo per decidere quale metodo utilizzare, ma per confrontare i propri dati e quindi decidere.


14
Se devi considerare una nuova riga sicura, cambia l'espressione in questa (cerca inversamente i file stampabili): preg_replace (/ [^ \ x0A \ x20- \ x7E] /, '', $ string);
Nick,

12
@Dalin Non esiste un "personaggio UTF-8". Ci sono simboli / caratteri Unicode e UTF-8 è una codifica che può rappresentarli tutti. Volevi dire che questo non funziona per i caratteri al di fuori del set di caratteri ASCII.
Mathias Bynens,

3
Se devi abbinare un carattere unicode sopra \ xFF, usa \ x {####}
Peter Olson il

ti sei perso \ x7F (127) che è un personaggio non stampabile
Mubashar

questo rimuoverà le lettere arabe, cattiva soluzione.
Ayman Hussein,

141

Molte delle altre risposte qui non tengono conto dei caratteri unicode (ad es. Öäüßйȝîûηы ე மி ᚉ ⠛). In questo caso è possibile utilizzare quanto segue:

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

C'è una strana classe di caratteri nell'intervallo \x80-\x9F(appena sopra l'intervallo di caratteri ASCII a 7 bit) che sono tecnicamente i caratteri di controllo, ma nel tempo sono stati utilizzati in modo improprio per i caratteri stampabili. Se non hai problemi con questi, puoi usare:

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

Se si desidera eliminare anche avanzamenti di riga, ritorni a capo, tabulazioni, spazi non interrotti e trattini morbidi, è possibile utilizzare:

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

Si noti che è necessario utilizzare virgolette singole per gli esempi sopra.

Se desideri rimuovere tutto tranne i caratteri ASCII stampabili di base (tutti i caratteri di esempio sopra verranno eliminati) puoi utilizzare:

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

Per riferimento consultare http://www.fileformat.info/info/charset/UTF-8/list.htm


1
Il tuo regexp gestisce bene i caratteri UTF8; ma rimuove i caratteri "speciali" non UTF8; come ç, ü e ö. '/[\x00-\x1F\x80-\xC0]/u'li lascia intatti; ma anche segno di divisione (F7) e moltiplicazione (D7).
Hazar,

@Hazar sì, hai ragione \ x80- \ xFF eliminato troppo, ma \ x80- \ xC0 è ancora troppo restrittivo. Questo perderebbe altri personaggi stampabili come © £ ±. Per riferimento vedi utf8-chartable.de
Dalin

1
@TimMalone perché PHP espanderà quelle sequenze di caratteri: php.net/manual/en/…, quindi il regex non vedrà l'intervallo di cui stai cercando di parlarne.
Dalin,

1
Che mi dici di 7F? Non dovrebbe essere \x7F-\x9F?
Bell,

1
Ho appena provato molto, ho provato ogni funzione di codifica disponibile in PHP da regex a mb_ a htmlspecialchars ecc. Nulla ha rimosso i caratteri di controllo, grazie per aver investito il lavoro.
Giovanni

29

A partire da PHP 5.2, abbiamo anche accesso a filter_var, di cui non ho visto alcuna menzione, quindi ho pensato di buttarlo lì. Per utilizzare filter_var per rimuovere i caratteri non stampabili <32 e> 127, è possibile eseguire:

Filtra i caratteri ASCII al di sotto di 32

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

Filtra i caratteri ASCII sopra 127

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

Striscia entrambi:

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

Puoi anche codificare html con caratteri bassi (newline, tab, ecc.) Mentre ti spogli in alto:

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

Esistono anche opzioni per la rimozione di HTML, la disinfezione di e-mail e URL, ecc. Quindi, molte opzioni per la disinfezione (eliminazione dei dati) e persino la convalida (restituiscono false se non valide piuttosto che eliminazioni silenziose).

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

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

Tuttavia, c'è ancora il problema, che FILTER_FLAG_STRIP_LOW eliminerà i ritorni a capo e a capo, che per un'area di testo sono caratteri completamente validi ... quindi alcune delle risposte Regex, immagino, sono ancora necessarie a volte, ad esempio dopo aver esaminato questo thread, ho intenzione di farlo per textareas:

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

Questo sembra più leggibile di un numero di regex che sono state eliminate dall'intervallo numerico.



18

questo è più semplice:

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


5
In questo modo vengono inoltre eliminati avanzamenti di riga, ritorni a capo e caratteri UTF8.
Dalin,

5
@Dalin Non esiste un "personaggio UTF-8". Ci sono simboli / caratteri Unicode e UTF-8 è una codifica che può rappresentarli tutti. Volevi dire che questo spoglia anche i caratteri al di fuori dell'intervallo ASCII .
Mathias Bynens,

1
Mangia personaggi arabi :)
Rolf,

16

Tutte le soluzioni funzionano parzialmente, e anche sotto probabilmente non coprono tutti i casi. Il mio problema era nel tentativo di inserire una stringa in una tabella mysql utf8. La stringa (e i suoi byte) erano tutti conformi a utf8, ma presentavano diverse sequenze errate. Presumo che la maggior parte di essi fosse controllo o formattazione.

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

Ad aggravare ulteriormente il problema è la tabella vs. server vs. connessione vs. rendering del contenuto, come abbiamo parlato un po 'qui


1
L'unico che ha superato tutti i miei test unitari, fantastico!
Korri,

\ xE2 \ x80 [\ xA4- \ xA8] (o 226.128. [164-168]) - è errato, la sequenza include i seguenti simboli stampabili: Carattere Unicode 'ONE DOT LEADER' (U + 2024), Carattere Unicode 'TWO DOT LEADER '(U + 2025), carattere Unicode' ELLIPSIS ORIZZONTALE '(U + 2026), carattere Unicode' HYPHENATION POINT '(U + 2027). E un solo non stampabile: il carattere Unicode 'LINE SEPARATOR' (U + 2028). Anche quello successivo non è stampabile: carattere Unicode 'PARAGRAPH SEPARATOR' (U + 2029). Quindi sostituisci la sequenza con: \ xE2 \ x80 [\ xA8- \ xA9] \ xE2 \ x80 [\ xA8- \ xA9] per rimuovere LINE SEPARATOR e PARAGRAPH SEPARATOR.
MingalevME,

Questa è la soluzione migliore che ho trovato finora, ma ho dovuto aggiungere Laso $s = preg_replace('/(\xF0\x9F[\x00-\xFF][\x00-\xFF])/', ' ', $s);perché tutti i personaggi emoji stavano facendo casino mysql
Joe Black,

9

La mia versione conforme UTF-8:

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


7
Questo rimuove bene caratteri come virgolette, parentesi, ecc. Questi sono certamente caratteri stampabili.
Gajus,

questo è meraviglioso! mi ha salvato la vita, incasinato durante la stampa di personaggi arabi, ha funzionato come un campione :)
Krishna

6

Puoi usare un espresso normale per rimuovere tutto tranne i personaggi che desideri conservare:

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

Sostituisce tutto ciò che non è (^) le lettere AZ o az, i numeri 0-9, lo spazio, il trattino basso, l'ipen, il segno più e la e commerciale - con nulla (cioè rimuovilo).


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

Ciò rimuoverà tutti i caratteri di controllo ( http://uk.php.net/manual/en/regexp.reference.unicode.php ) lasciando i \ncaratteri di nuova riga. Dalla mia esperienza, i caratteri di controllo sono quelli che più spesso causano i problemi di stampa.


1
Funziona perfettamente per me! Ho aggiunto solo /uper i caratteri UTF-8. Potresti spiegare cosa fa la prima parte (?!\n)?
Marcio Mazzucato,

4

Per eliminare tutti i caratteri non ASCII dalla stringa di input

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

Quel codice rimuove tutti i caratteri negli intervalli esadecimali 0-31 e 128-255, lasciando solo i caratteri esadecimali 32-127 nella stringa risultante, che chiamo $ risultato in questo esempio.


3

La risposta di @PaulDixon è completamente sbagliata , perché rimuove i caratteri ASCII estesi stampabili 128-255! è stato parzialmente corretto. Non so perché voglia ancora eliminare 128-255 da un set ASCII a 7 bit a 127 caratteri in quanto non ha i caratteri ASCII estesi.

Ma alla fine è stato importante non cancellare 128-255 perché ad esempio chr(128)( \x80) è il simbolo dell'euro in ASCII a 8 bit e molti caratteri UTF-8 in Windows mostrano un simbolo dell'euro e Android per quanto riguarda il mio test.

E ucciderà molti caratteri UTF-8 se rimuovete i caratteri ASCII 128-255 da una stringa UTF-8 (probabilmente i byte iniziali di un carattere UTF-8 multi-byte). Quindi non farlo! Sono caratteri completamente legali in tutti i file system attualmente utilizzati. L'unico intervallo riservato è 0-31 .

Invece usa questo per cancellare i caratteri non stampabili 0-31 e 127:

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

Si lavora in ASCII e UTF-8 perché entrambi condividono la stessa gamma di set di controlli .

L' alternativa più lenta più veloce¹ senza usare espressioni regolari:

$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 si desidera mantenere tutti i caratteri di spaziatura \t, \ne \r, quindi rimuovere chr(9), chr(10)e chr(13)da questo elenco. Nota: il solito spazio bianco è chr(32)quindi rimane nel risultato. Decidi te stesso se vuoi rimuovere lo spazio non interrotto chr(160)in quanto può causare problemi.

¹ Testato da @PaulDixon e verificato da me stesso.


2

che ne dite di:

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

mi dà il controllo completo di ciò che voglio includere


0

La risposta contrassegnata è perfetta ma manca il carattere 127 (DEL) che è anche un personaggio non stampabile

la mia risposta sarebbe

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

Anche questa risposta è sbagliata. Vedi: stackoverflow.com/a/42058165/318765
mgutt

la risposta sopra era un complimento alla risposta originale che aggiunge solo il carattere "elimina".
Mubashar

0

"Cedivad" ha risolto il problema con il persistente risultato dei caratteri svedesi ÅÄÖ.

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

Grazie!


0

Per chiunque stia ancora cercando di farlo senza rimuovere i caratteri non stampabili, ma piuttosto sfuggendoli, l'ho fatto per dare una mano. Sentiti libero di migliorarlo! I caratteri vengono salvati in \\ x [A-F0-9] [A-F0-9].

Chiama così:

$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

La regex nella risposta selezionata non riesce per Unicode: 0x1d (con php 7.4)

una soluzione:

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

da: UTF 8 String rimuove tutti i caratteri invisibili tranne newline

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.