PHP: Wie entferne ich alle nicht druckbaren Zeichen in einer Zeichenfolge?

158

Ich stelle mir vor, ich muss die Zeichen 0-31 und 127 entfernen.

Gibt es eine Funktion oder einen Code, um dies effizient zu tun?

Stewart Robinson
quelle

Antworten:

353

7 Bit ASCII?

Wenn Ihre Tardis gerade 1963 gelandet ist und Sie nur die 7-Bit-druckbaren ASCII-Zeichen möchten, können Sie Folgendes von 0-31 und 127-255 herausreißen:

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

Es stimmt mit allem in den Bereichen 0-31, 127-255 überein und entfernt es.

8 Bit erweitertes ASCII?

Sie sind in eine Whirlpool-Zeitmaschine gefallen und in den achtziger Jahren zurück. Wenn Sie eine Form von 8-Bit-ASCII haben, möchten Sie möglicherweise die Zeichen im Bereich von 128 bis 255 halten. Eine einfache Einstellung - suchen Sie einfach nach 0-31 und 127

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

UTF-8?

Ah, willkommen zurück im 21. Jahrhundert. Wenn Sie eine UTF-8-codierte Zeichenfolge haben, kann der /u Modifikator für die Regex verwendet werden

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

Dadurch werden nur 0-31 und 127 entfernt. Dies funktioniert in ASCII und UTF-8, da beide denselben Kontrollsatzbereich verwenden (wie von mgutt unten angegeben). Genau genommen würde dies ohne den /uModifikator funktionieren . Aber es macht das Leben leichter, wenn Sie andere Zeichen entfernen möchten ...

Wenn Sie mit Unicode arbeiten, gibt es möglicherweise viele nicht druckbare Elemente . Betrachten wir jedoch ein einfaches: NO-BREAK SPACE (U + 00A0)

In einer UTF-8-Zeichenfolge würde dies als codiert 0xC2A0. Sie können diese bestimmte Sequenz suchen und entfernen, aber mit dem /uModifikator können Sie einfach \xA0die Zeichenklasse hinzufügen :

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

Nachtrag: Was ist mit str_replace?

preg_replace ist ziemlich effizient, aber wenn Sie diesen Vorgang häufig ausführen, können Sie ein Array von Zeichen erstellen, die Sie entfernen möchten, und str_replace verwenden, wie von mgutt unten angegeben, z

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

Intuitiv scheint dies schnell zu sein, aber es ist nicht immer der Fall. Sie sollten auf jeden Fall einen Benchmark erstellen, um zu sehen, ob Sie dadurch etwas sparen. Ich habe einige Benchmarks über eine Vielzahl von Stringlängen mit zufälligen Daten durchgeführt, und dieses Muster wurde mit PHP 7.0.12 erstellt

     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

Die Timings selbst beziehen sich auf 10000 Iterationen, aber was interessanter ist, sind die relativen Unterschiede. Bis zu 512 Zeichen sah ich immer, wie preg_replace gewann. Im Bereich von 1 bis 8 KB hatte str_replace eine Randkante.

Ich fand das Ergebnis interessant und habe es hier aufgenommen. Das Wichtigste ist nicht, dieses Ergebnis zu verwenden, um zu entscheiden, welche Methode verwendet werden soll, sondern um einen Benchmark mit Ihren eigenen Daten durchzuführen und dann zu entscheiden.

Paul Dixon
quelle
14
Wenn Sie einen Newline-Safe in Betracht ziehen müssen, ändern Sie den Ausdruck in diesen (umgekehrt nach Ausdrucken suchen): preg_replace (/ [^ \ x0A \ x20- \ x7E] /, '', $ string);
Nick
12
@Dalin Es gibt kein "UTF-8-Zeichen". Es gibt Unicode-Symbole / -Zeichen, und UTF-8 ist eine Codierung, die alle darstellen kann. Sie wollten damit sagen, dass dies für Zeichen außerhalb des ASCII-Zeichensatzes nicht funktioniert.
Mathias Bynens
3
Wenn Sie ein Unicode-Zeichen über \ xFF finden müssen, verwenden Sie \ x {####}
Peter Olson
Sie haben \ x7F (127) verpasst, ein nicht druckbares Zeichen
Mubashar
Dies wird arabische Buchstaben entfernen, schlechte Lösung.
Ayman Hussein
141

Viele der anderen Antworten hier berücksichtigen keine Unicode-Zeichen (z. B. öäüßйȝîûηы ე மி ᚉ ⠛). In diesem Fall können Sie Folgendes verwenden:

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

Es gibt eine seltsame Klasse von Zeichen im Bereich \x80-\x9F(knapp über dem 7-Bit-ASCII-Zeichenbereich), die technisch kontrollierte Zeichen sind, aber im Laufe der Zeit für druckbare Zeichen missbraucht wurden. Wenn Sie damit keine Probleme haben, können Sie Folgendes verwenden:

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

Wenn Sie auch Zeilenvorschübe, Wagenrückläufe, Tabulatoren, nicht unterbrechende Leerzeichen und weiche Bindestriche entfernen möchten, können Sie Folgendes verwenden:

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

Beachten Sie, dass Sie für die obigen Beispiele einfache Anführungszeichen verwenden müssen .

Wenn Sie alles außer einfachen druckbaren ASCII-Zeichen entfernen möchten (alle obigen Beispielzeichen werden entfernt), können Sie Folgendes verwenden:

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

Referenz siehe http://www.fileformat.info/info/charset/UTF-8/list.htm

Dalin
quelle
1
Ihr regulärer Ausdruck verarbeitet UTF8-Zeichen einwandfrei. Nicht-UTF8-Sonderzeichen werden jedoch entfernt. wie ç, ü und ö. '/[\x00-\x1F\x80-\xC0]/u'lässt sie intakt; aber auch Divisions- (F7) und Multiplikationszeichen (D7).
Hazar
@Hazar ja du bist richtig \ x80- \ xFF hat zu viel entfernt, aber \ x80- \ xC0 ist immer noch zu restriktiv. Dies würde andere druckbare Zeichen wie © £ ± vermissen. Referenz siehe utf8-chartable.de
Dalin
1
@TimMalone, da PHP diese Zeichenfolgen erweitert: php.net/manual/en/…, sodass der Regex nicht den Bereich sieht, über den Sie ihn erzählen möchten .
Dalin
1
Was ist mit 7F? Sollte es nicht sein \x7F-\x9F?
Bell
1
Ich habe nur viel versucht, ich habe jede in PHP verfügbare Codierungsfunktion ausprobiert, von Regex über mb_ bis hin zu HTML-Spezialchars usw. Nichts hat Steuerzeichen entfernt, danke für die Investition der Arbeit.
John
29

Ab PHP 5.2 haben wir auch Zugriff auf filter_var, von dem ich keine Erwähnung gesehen habe, also dachte ich, ich würde es da rauswerfen. So verwenden Sie filter_var, um nicht druckbare Zeichen <32 und> 127 zu entfernen:

Filtern Sie ASCII-Zeichen unter 32

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

Filtern Sie ASCII-Zeichen über 127

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

Ziehe beide aus:

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

Sie können auch niedrige Zeichen (Zeilenumbruch, Tabulator usw.) in HTML codieren, während Sie hohe Zeichen entfernen:

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

Es gibt auch Optionen zum Entfernen von HTML, zum Bereinigen von E-Mails und URLs usw. Es gibt also viele Optionen zum Bereinigen (Daten entfernen) und sogar zur Validierung (Rückgabe false, wenn nicht gültig, anstatt stillschweigend zu entfernen).

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

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

Es besteht jedoch immer noch das Problem, dass FILTER_FLAG_STRIP_LOW Zeilenumbrüche und Zeilenumbrüche entfernt, die für einen Textbereich vollständig gültige Zeichen sind. Einige der Regex-Antworten sind daher manchmal noch erforderlich, z. B. nach Überprüfung Thread, ich habe vor, dies für Textbereiche zu tun:

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

Dies scheint besser lesbar zu sein als eine Reihe von regulären Ausdrücken, die durch den numerischen Bereich entfernt wurden.

Kevin Nelson
quelle
27

Sie können Zeichenklassen verwenden

/[[:cntrl:]]+/
Ghostdog74
quelle
Muss ich dafür nicht Ereg verwenden?
Stewart Robinson
18

das ist einfacher:

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

Jacktrade
quelle
5
Dadurch werden auch Zeilenvorschübe, Wagenrückläufe und UTF8-Zeichen entfernt.
Dalin
5
@Dalin Es gibt kein "UTF-8-Zeichen". Es gibt Unicode-Symbole / -Zeichen, und UTF-8 ist eine Codierung, die alle darstellen kann. Sie wollten damit sagen, dass dadurch auch Zeichen außerhalb des ASCII-Bereichs entfernt werden.
Mathias Bynens
1
Isst arabische Zeichen auf :)
Rolf
16

Alle Lösungen funktionieren teilweise, und selbst im Folgenden werden wahrscheinlich nicht alle Fälle abgedeckt. Mein Problem bestand darin, einen String in eine utf8-MySQL-Tabelle einzufügen. Die Zeichenfolge (und ihre Bytes) entsprachen alle utf8, hatten jedoch mehrere fehlerhafte Sequenzen. Ich gehe davon aus, dass die meisten von ihnen Kontrolle oder Formatierung waren.

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

Um das Problem weiter zu verschärfen, ist die Tabelle vs. Server vs. Verbindung vs. Rendering des Inhalts, wie hier ein wenig erwähnt

Wayne Weibel
quelle
1
Der einzige, der alle meine Unit-Tests besteht, großartig!
Korri
\ xE2 \ x80 [\ xA4- \ xA8] (oder 226.128. [164-168]) - ist falsch, die Reihenfolge enthält die nächsten druckbaren Symbole: Unicode-Zeichen 'ONE DOT LEADER' (U + 2024), Unicode-Zeichen 'TWO DOT LEADER '(U + 2025), Unicode-Zeichen' HORIZONTAL ELLIPSIS '(U + 2026), Unicode-Zeichen' HYPHENATION POINT '(U + 2027). Und nur ein nicht druckbares: Unicode-Zeichen 'LINE SEPARATOR' (U + 2028). Das nächste ist ebenfalls nicht druckbar: Unicode-Zeichen 'PARAGRAPH SEPARATOR' (U + 2029). Ersetzen Sie daher die Sequenz durch: \ xE2 \ x80 [\ xA8- \ xA9] \ xE2 \ x80 [\ xA8- \ xA9], um LINE SEPARATOR und PARAGRAPH SEPARATOR zu entfernen.
MingalevME
Dies ist die beste Lösung, die ich bisher finden konnte, aber ich musste Laso hinzufügen, $s = preg_replace('/(\xF0\x9F[\x00-\xFF][\x00-\xFF])/', ' ', $s);weil alle Emoji-Charaktere MySQL durcheinander brachten
Joe Black
9

Meine UTF-8-kompatible Version:

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

Cedivad
quelle
7
Dadurch werden Zeichen wie Anführungszeichen, Klammern usw. entfernt. Dies sind sicherlich druckbare Zeichen.
Gajus
das ist wunderbar! es hat mein Leben gerettet, beim Drucken arabischer Schriftzeichen durcheinander gebracht, hat wie ein Champion funktioniert :)
krishna
6

Sie können einen regulären Express verwenden, um alles außer den Zeichen zu entfernen, die Sie behalten möchten:

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

Ersetzt alles, was nicht (^) die Buchstaben AZ oder az, die Zahlen 0-9, Leerzeichen, Unterstrich, Hypen, Plus und kaufmännisches Und ist - durch nichts (dh entfernen Sie es).

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

Dadurch werden alle Steuerzeichen ( http://uk.php.net/manual/en/regexp.reference.unicode.php ) entfernt, wobei die \nZeilenumbruchzeichen verbleiben. Nach meiner Erfahrung sind die Steuerzeichen diejenigen, die am häufigsten die Druckprobleme verursachen.

Gajus
quelle
1
Es funktioniert perfekt für mich! Ich habe nur /ufür UTF-8-Zeichen hinzugefügt . Könnten Sie bitte erklären, was der erste Teil (?!\n)tut?
Marcio Mazzucato
4

So entfernen Sie alle Nicht-ASCII-Zeichen aus der Eingabezeichenfolge

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

Dieser Code entfernt alle Zeichen in den Hex-Bereichen 0-31 und 128-255, wobei nur die Hex-Zeichen 32-127 in der resultierenden Zeichenfolge verbleiben, die ich in diesem Beispiel $ result nenne.

Junaid Masood
quelle
3

Die Antwort von @PaulDixon ist völlig falsch , da die druckbaren erweiterten ASCII-Zeichen 128-255 entfernt werden! wurde teilweise korrigiert. Ich weiß nicht, warum er immer noch 128-255 aus einem 7-Bit-ASCII-Satz mit 127 Zeichen löschen möchte, da er nicht die erweiterten ASCII-Zeichen enthält.

Aber schließlich war es wichtig, 128-255 nicht zu löschen, da zum Beispiel chr(128)( \x80) das Euro-Zeichen in 8-Bit-ASCII ist und viele UTF-8-Schriftarten in Windows ein Euro-Zeichen und Android in Bezug auf meinen eigenen Test anzeigen .

Und es werden viele UTF-8-Zeichen getötet, wenn Sie die ASCII-Zeichen 128-255 aus einer UTF-8-Zeichenfolge entfernen (wahrscheinlich die Startbytes eines Multi-Byte-UTF-8-Zeichens). Also tu das nicht! Sie sind in allen derzeit verwendeten Dateisystemen völlig legal. Der einzige reservierte Bereich ist 0-31 .

Verwenden Sie stattdessen diese Option, um die nicht druckbaren Zeichen 0-31 und 127 zu löschen:

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

Es funktioniert in ASCII und UTF-8, da beide denselben Kontrollsatzbereich verwenden .

Die schnellste langsamere Alternative ohne reguläre Ausdrücke:

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

Wenn Sie alle Leerzeichen behalten wollen \t, \nund \rdann entfernen chr(9), chr(10)und chr(13)aus dieser Liste. Hinweis: Das übliche Leerzeichen ist chr(32)so, dass es im Ergebnis bleibt. Entscheiden Sie selbst, ob Sie nicht unterbrechenden Speicherplatz entfernen möchten, chr(160)da dies zu Problemen führen kann.

¹ Von @PaulDixon getestet und von mir selbst verifiziert.

mgutt
quelle
2

wie wäre es mit:

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

gibt mir die vollständige Kontrolle darüber, was ich einschließen möchte

sdfor
quelle
0

Die markierte Antwort ist perfekt, es fehlt jedoch das Zeichen 127 (DEL), das ebenfalls nicht druckbar ist

Meine Antwort wäre

$string = preg_replace('/[\x00-\x1F\x7f-\xFF]/', '', $string);
Mubashar
quelle
Diese Antwort ist auch falsch. Siehe: stackoverflow.com/a/42058165/318765
mgutt
Die obige Antwort war ein Kompliment an die ursprüngliche Antwort, die nur das Zeichen "Löschen" ergibt.
Mubashar
0

"cedivad" löste das Problem für mich mit anhaltendem Ergebnis der schwedischen Zeichen ÅÄÖ.

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

Vielen Dank!

Andreas Ek
quelle
0

Für alle, die immer noch nach Möglichkeiten suchen, ohne die nicht druckbaren Zeichen zu entfernen, sondern ihnen zu entkommen, habe ich dies gemacht, um zu helfen. Fühlen Sie sich frei, es zu verbessern! Zeichen werden nach \\ x [A-F0-9] [A-F0-9] maskiert.

Rufen Sie so an:

$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));
    }
?>
Lass es fallen als sei es heiß
quelle
0

Ich habe das Problem für UTF8 mit https://github.com/neitanod/forceutf8 gelöst

use ForceUTF8\Encoding;

$string = Encoding::fixUTF8($string);
mnv
quelle
1
Diese Bibliothek konvertiert Zeichen mit UTF-8-Akzent und UTF-8-Emoticons in "?" Symbole. Ziemlich ernstes Problem leider.
ChristoKiwi
0

Der Regex in der ausgewählten Antwort schlägt für Unicode fehl: 0x1d (mit PHP 7.4)

eine Lösung:

<?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 entfernt alle unsichtbaren Zeichen außer Zeilenumbruch

Mkdgs
quelle