PHP: Bir dizedeki yazdırılamayan tüm karakterler nasıl kaldırılır?


161

0-31 ve 127 karakterlerini kaldırmam gerektiğini düşünüyorum,

Bunu verimli bir şekilde yapmak için bir fonksiyon veya kod parçası var mı?

Yanıtlar:


355

7 bit ASCII?

Tardis'iniz 1963'te indi ve sadece 7 bitlik yazdırılabilir ASCII karakterlerini istiyorsanız, 0-31 ve 127-255 arasındaki her şeyi şununla sökebilirsiniz:

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

0-31, 127-255 aralığındaki herhangi bir şeyle eşleşir ve kaldırır.

8 bit genişletilmiş ASCII?

Bir Hot Tub Zaman Makinesine düştünüz ve seksenlere geri döndünüz. Bir çeşit 8 bit ASCII'niz varsa, karakterleri 128-255 aralığında tutmak isteyebilirsiniz. Kolay ayar - sadece 0-31 ve 127'yi arayın

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

UTF-8?

Ah, 21. yüzyıla tekrar hoş geldiniz. UTF-8 kodlu bir dizeniz varsa, /u değiştirici normal ifadede kullanılabilir

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

Bu sadece 0-31 ve 127'yi kaldırır. ASCII ve UTF-8'de çalışır, çünkü her ikisi de aynı kontrol seti aralığını paylaşır (aşağıdaki mgutt tarafından belirtildiği gibi). Kesinlikle, bu /udeğiştirici olmadan çalışır . Ancak diğer karakterleri kaldırmak isterseniz hayatı kolaylaştırır ...

Unicode ile uğraşıyorsanız, potansiyel olarak birçok baskı dışı öğe var , ancak basit bir tane düşünelim: NO-BREAK SPACE (U + 00A0)

UTF-8 dizgisinde bu olarak kodlanır 0xC2A0. Bu belirli diziyi /uarayabilir ve kaldırabilirsiniz, ancak değiştirici yerinde \xA0iken, karakter sınıfına ekleyebilirsiniz :

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

Ek: Peki ya str_replace?

preg_replace oldukça verimlidir, ancak bu işlemi çok yapıyorsanız, kaldırmak istediğiniz karakter dizisini oluşturabilir ve aşağıdaki mgutt tarafından belirtildiği gibi str_replace kullanabilirsiniz.

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

Sezgisel olarak, bu hızlı olacak gibi görünüyor, ama her zaman böyle değil, kesinlikle bir şey kaydedip kaydetmediğini görmek için kesinlikle kıyaslamalısınız. Rasgele veriler ile çeşitli dize uzunlukları arasında bazı kriterler yaptım ve bu desen php 7.0.12 kullanılarak ortaya çıktı

     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

Zamanlamalar 10000 yineleme içindir, ancak daha ilginç olan göreceli farklılıklardır. 512 karaktere kadar, preg_replace alway galibiyeti görüyordum. 1-8kb aralığında, str_replace kenar boşluğuna sahipti.

İlginç bir sonuç olduğunu düşündüm, bu yüzden buraya dahil ettim. Önemli olan bu sonucu almak ve hangi yöntemi kullanacağına karar vermek değil, kendi verilerinizle kıyaslamak ve sonra karar vermektir.


14
Yeni satırın güvenli olduğunu düşünmeniz gerekiyorsa, ifadeyi şu şekilde değiştirin (tersine yazdırılabilir yazı arayın): preg_replace (/ [^ \ x0A \ x20- \ x7E] /, '', $ string);
Nick

12
@Dalin “UTF-8 karakteri” diye bir şey yoktur. Unicode sembolleri / karakterleri vardır ve UTF-8, hepsini temsil edebilecek bir kodlamadır. Bunun ASCII karakter kümesi dışındaki karakterler için işe yaramadığını söylemek istediniz.
Mathias Bynens

3
\ XFF üzerindeki bir unicode karakteri eşleştirmeniz gerekiyorsa, \ x {####} kullanın
Peter Olson

olmayan bir yazdırılabilir karakter \ x7F (127) cevapsız
Mubashar

Bu Arapça harfleri, kötü çözümü kaldıracak.
Ayman Hussein

141

Buradaki diğer cevapların çoğu unicode karakterleri dikkate almaz (örn. Öäüßйȝîûηы ე மி ᚉ ⠛). Bu durumda aşağıdakileri kullanabilirsiniz:

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

\x80-\x9FAralıkta teknik olarak kontrol karakterleri olan garip bir karakter sınıfı var ( 7 bitlik ASCII karakter aralığının hemen üstünde), ancak zaman içinde yazdırılabilir karakterler için kötüye kullanıldı. Bunlarla ilgili herhangi bir sorununuz yoksa, şunları kullanabilirsiniz:

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

Satır beslemelerini, satır başlarını, sekmeleri, kırılmayan boşlukları ve yumuşak kısa çizgileri de şeritlemek istiyorsanız, şunları kullanabilirsiniz:

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

Yukarıdaki örnekler için tek tırnak kullanmanız gerektiğini unutmayın .

Temel yazdırılabilir ASCII karakterleri hariç her şeyi soymak istiyorsanız (yukarıdaki tüm örnek karakterler çıkarılır) kullanabilirsiniz:

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

Referans için bkz. Http://www.fileformat.info/info/charset/UTF-8/list.htm


1
Normal ifadeniz UTF8 karakterlerini iyi işler; ancak UTF8 dışı "özel" karakterler çıkarır; ç, ü ve ö gibi. '/[\x00-\x1F\x80-\xC0]/u'onları sağlam bırakır; aynı zamanda bölme (F7) ve çarpma (D7) işareti.
Hazar

@Hazar evet haklısın \ x80- \ xFF çok fazla soyuldu, ama \ x80- \ xC0 hala çok kısıtlayıcı. Bu, © £ ± gibi yazdırılabilir diğer karakterleri de kaçırır. Referans için bakınız utf8-chartable.de
Dalin

1
@TimMalone, çünkü PHP bu karakter dizilerini genişletecek: php.net/manual/en/…, böylece normal ifade anlatmaya çalıştığınız aralığı görmeyecek.
Dalin

1
7F ne olacak? Olmamalı mı \x7F-\x9F?
Bell

1
Ben sadece çok denedim, ben regex mb_ htmlspecialchars vb PHP mevcut her kodlama fonksiyonu denedim. Hiçbir şey kontrol karakterleri kaldırıldı, iş yatırım için teşekkürler.
John

29

PHP 5.2 ile başlayarak, orada atmak istiyorum diye düşündüm herhangi bir söz görmedim filter_var erişim var. <32 ve> 127 yazdırılamayan karakterleri ayırmak için filter_var kullanmak için şunları yapabilirsiniz:

32'nin altındaki ASCII karakterlerine filtre uygulama

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

127'nin üzerindeki ASCII karakterlerine filtre uygulama

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

Her ikisini de soyun:

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

Ayrıca, yüksek sıyrılarak düşük karakterleri (satırsonu, sekme vb.) Html ile kodlayabilirsiniz:

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

HTML'yi sıyırma, e-postaları ve URL'leri sterilize etme, vb. Seçenekleri de vardır.

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

Doğrulama: http://php.net/manual/en/filter.filters.validate.php

Bununla birlikte, hala bir sorun var, FILTER_FLAG_STRIP_LOW bir textarea için tamamen geçerli karakterler olan satırsonu ve satır başı döndürmelerini çıkaracaktır ... iplik, bunu textareas için yapmayı planlıyorum:

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

Bu, sayısal aralıkla çıkarılan bir dizi normalden daha okunabilir görünüyor.



18

bu daha basit:

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


5
Bu aynı zamanda satır beslemelerini, satır başlarını ve UTF8 karakterlerini de şeritler.
Dalin

5
@Dalin “UTF-8 karakteri” diye bir şey yoktur. Unicode sembolleri / karakterleri vardır ve UTF-8, hepsini temsil edebilecek bir kodlamadır. Bunun karakterleri ASCII aralığının dışında bıraktığını söylemek istediniz .
Mathias Bynens

1
Arapça karakterleri yiyor :)
Rolf

16

Tüm çözümler kısmen çalışıyor ve aşağıda bile muhtemelen tüm vakaları kapsamıyor. Benim sorunum bir utf8 mysql tabloya bir dize eklemek çalışıyordu. Dize (ve baytları) utf8 ile uyumludur, ancak birkaç kötü diziye sahiptir. Çoğunun kontrol veya biçimlendirme olduğunu varsayıyorum.

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

Sorunu daha da kötüleştirmek için, tablodan sunucuya, içeriğin bağlantısına ve oluşturulmasına, burada biraz bahsedildiği gibi


1
Tüm birim testlerimi geçen tek kişi, harika!
Korri

\ xE2 \ x80 [\ xA4- \ xA8] (veya 226.128. [164-168]) - yanlış, sıra sonraki yazdırılabilir sembolleri içeriyor: Unicode Karakter 'ONE DOT LEADER' (U + 2024), Unicode Karakter 'İKİ NOKTA LİDER '(U + 2025), Unicode Karakter' YATAY ELLIPSIS '(U + 2026), Unicode Karakter' HİFENASYON NOKTASI '(U + 2027). Ve sadece bir tane yazdırılamaz: Unicode Karakter 'LINE SEPARATOR' (U + 2028). Sonraki karakter de yazdırılamaz: Unicode Karakter 'PARAGRAPH SEPARATOR' (U + 2029). Bu nedenle, LINE SEPARATOR ve PARAGRAPH SEPARATOR'u kaldırmak için diziyi \ xE2 \ x80 [\ xA8- \ xA9] \ xE2 \ x80 [\ xA8- \ xA9] ile değiştirin.
MingalevME

Bu şimdiye kadar bulabildiğim en iyi çözüm, ama laso $s = preg_replace('/(\xF0\x9F[\x00-\xFF][\x00-\xFF])/', ' ', $s);tüm emoji karakterleri mysql berbat olduğu için eklemek zorunda kaldım
Joe Black

10

UTF-8 uyumlu sürümüm:

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


7
Bu, tırnak işaretleri, köşeli ayraçlar, vb. Gibi karakterleri kaldırın. Bunlar kesinlikle yazdırılabilir karakterlerdir.
Gajus

bu harika! hayatımı kurtardı, Arapça karakterler yazdırırken berbat, şampiyon gibi çalıştı :)
krishna

6

Saklamak istediğiniz karakterlerin dışındaki her şeyi kaldırmak için normal bir ifade kullanabilirsiniz:

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

AZ veya az harfleri (^), 0-9 sayıları, boşluk, alt çizgi, hiper, artı ve ve işareti olmayan her şeyi değiştirir (yani kaldırın).


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

Bu , yeni satır karakterlerini bırakarak tüm kontrol karakterlerini ( http://uk.php.net/manual/en/regexp.reference.unicode.php ) kaldıracaktır \n. Deneyimlerime göre, kontrol karakterleri en sık yazdırma sorunlarına neden olan karakterlerdir.


1
Benim için mükemmel çalışıyor! Sadece /uUTF-8 karakterleri için ekledim . İlk bölümün ne yaptığını açıklar (?!\n)mısınız?
Marcio Mazzucato

4

ASCII olmayan tüm karakterleri giriş dizesinden çıkarmak için

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

Bu kod, bu örnekte $ sonuç olarak adlandırdığım sonuç dizesinde yalnızca 32-127 hex karakterlerini bırakarak onaltılık aralıklarda 0-31 ve 128-255 karakterleri kaldırır.


3

@PaulDixon cevabı ise tamamen yanlış çünkü, yazdırılabilir kaldırır genişletilmiş ASCII karakterleri 128-255! kısmen düzeltildi. Neden hala genişletilmiş ASCII karakterleri olmadığı için 127 karakterlik 7 bitlik ASCII setinden 128-255'i silmek istediğini bilmiyorum.

Ancak son olarak 128-255'i silmemek önemliydi çünkü örneğin chr(128)( \x80) 8-bit ASCII'de euro işareti ve Windows'taki birçok UTF-8 yazı tipi kendi testimle ilgili bir euro işareti ve Android gösteriyor.

Ve ASCII karakterlerini 128-255 bir UTF-8 dizesinden (muhtemelen çok baytlık bir UTF-8 karakterinin başlangıç ​​baytları) kaldırırsanız, birçok UTF-8 karakterini öldürür. Öyleyse yapma! Şu anda kullanılan tüm dosya sistemlerinde tamamen yasal karakterlerdir. Tek ayrılmış aralık 0-31'dir .

Bunun yerine, yazdırılamayan 0-31 ve 127 karakterlerini silmek için bunu kullanın:

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

Her ikisi de aynı kontrol seti aralığını paylaştığı için ASCII ve UTF-8'de çalışır .

En hızlı normal ifadeler kullanmadan slower¹ alternatif:

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

Eğer tüm boş karakterleri tutmak istiyorsanız \t, \nve \rdaha sonra kaldırmak chr(9), chr(10)ve chr(13)bu listeden. Not: Her zamanki boşluk chr(32)böylece sonuçta kalır. chr(160)Sorunlara neden olabileceğinden kırılmayan alanı kaldırmak isteyip istemediğinize kendiniz karar verin .

@ @PaulDixon tarafından test edildi ve kendim doğruladım.


2

nasıl:

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

bana neleri dahil etmek istediğimin tam kontrolünü veriyor


0

İşaretli anwser mükemmel, ancak yazdırılamayan bir karakter olan 127 (DEL) karakterini de kaçırıyor

cevabım olurdu

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

Bu cevap da yanlış. Bakınız: stackoverflow.com/a/42058165/318765
mgutt

yukarıdaki cevap sadece "sil" karakteri ekleyen orijinal yanıta bir iltifattı.
Mubashar

0

"cedivad", İsveçli chars ÅÄÖ'nin kalıcı sonucu ile benim için sorunu çözdü.

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

Teşekkürler!


0

Yazdırılamayan karakterleri kaldırmadan bunu nasıl yapacağını arayan, ancak onlardan kaçan herkes için, bunu yardımcı olmak için yaptım. Geliştirmek için çekinmeyin! Karakterler \\ x [A-F0-9] [A-F0-9] karakterinden kaçıyor.

Şöyle arayın:

$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

Seçilen cevaba yapılan normal ifade Unicode için başarısız oldu: 0x1d (php 7.4 ile)

bir çözüm:

<?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 Dize newline hariç tüm görünmez karakterleri kaldır

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.