PHP:如何删除字符串中所有不可打印的字符?


Answers:


354

7位ASCII?

如果您的Tardis刚好在1963年登陆,并且您只想要7位可打印的ASCII字符,则可以使用以下方法从0-31和127-255中删除所有内容:

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

它匹配0-31、127-255范围内的任何内容并将其删除。

8位扩展ASCII?

您掉进了热水浴缸计时机,而您又回到了八十年代。如果您具有某种形式的8位ASCII,那么您可能希望将字符保持在128-255范围内。轻松调整-只需查找0-31和127

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

UTF-8?

啊,欢迎回到21世纪。如果您使用UTF-8编码的字符串,则可以在正则表达式上使用/u 修饰符

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

这只会删除0-31和127。这可用于ASCII和UTF-8,因为它们共享相同的控件集范围(如下面的mgutt所述)。严格来说,如果没有/u修饰符,这将起作用。但是,如果您要删除其他字符,它会使生活更轻松。

如果您要处理Unicode,则可能有很多非打印元素,但让我们考虑一个简单的元素NO-BREAK SPACE(U + 00A0)

在UTF-8字符串中,该编码为0xC2A0。您可以查找并删除该特定序列,但是/u只要有了修饰符,您就可以简单地添加\xA0到字符类中:

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

附录:str_replace呢?

preg_replace非常有效,但是如果您经常执行此操作,则可以构建要删除的字符数组,并使用下面的mgutt指出的str_replace,例如

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

直觉上,这似乎会很快,但并非总是如此,您绝对应该进行基准测试,看看它是否可以为您节省任何费用。我使用随机数据在各种字符串长度上进行了一些基准测试,并且使用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

计时本身是10000次迭代,但更有趣的是相对差异。最多512个字符,我一直看到preg_replace赢了。在1-8kb的范围内,str_replace具有边沿边缘。

我认为这是一个有趣的结果,因此将其包含在此处。重要的不是获取此结果并使用它来决定使用哪种方法,而是对照您自己的数据进行基准测试然后再决定。


14
如果需要考虑换行符安全,请将表达式更改为此(反向搜索可打印对象):preg_replace(/ [^ \ x0A \ x20- \ x7E] /,'',$ string);
尼克,2010年

12
@Dalin没有“ UTF-8字符”之类的东西。有Unicode符号/字符,而UTF-8是可以代表所有符号的编码。您的意思是说这不适用于ASCII字符集以外的字符。
Mathias Bynens

3
如果您需要在\ xFF上方匹配unicode字符,请使用\ x {####}
彼得·奥尔森

您错过了\ x7F(127)这是不可打印的字符
Mubashar 2013年

这将删除阿拉伯字母,不好的解决办法。
艾曼·侯赛因

141

此处的许多其他答案都未考虑Unicode字符(例如öäüßйȝîûηыეமிᚉ⠛)。在这种情况下,您可以使用以下方法:

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

该范围内有一个奇怪的字符类别 \x80-\x9F(在7位ASCII字符范围内)技术上是控制字符,但是随着时间的流逝,它们被误用于可打印字符。如果这些没有任何问题,则可以使用:

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

如果您还希望删除换行符,回车符,制表符,不间断空格和软连字符,则可以使用:

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

注意你 以上示例必须使用单引号。

如果您希望剥离除基本可打印ASCII字符以外的所有内容(上述所有示例字符都将被剥离),则可以使用:

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

有关参考,请参见http://www.fileformat.info/info/charset/UTF-8/list.htm


1
您的正则表达式可以很好地处理UTF8字符;但它会去除非UTF8的“特殊”字符;像ç,ü和ö。'/[\x00-\x1F\x80-\xC0]/u'使它们完好无损;而且还有除法(F7)和乘法(D7)符号。
哈扎尔

@哈扎尔是的,您是正确的\ x80- \ xFF去除过多,但是\ x80- \ xC0仍然过于严格。这会错过其他可打印字符,例如©£±。供参考,请参阅utf8-chartable.de
大林镇

1
@TimMalone,因为PHP将扩展这些字符序列:php.net/manual/en/…因此,正则表达式将看不到您要说明的范围。
大林

1
7F呢?应该不是\x7F-\x9F吗?
贝尔

1
我只是尝试了很多,我尝试了PHP中可用的每个编码功能,从正则表达式到mb_到htmlspecialchars等。什么都没有删除控制字符,感谢您的投入。
约翰(John John)

29

从PHP 5.2开始,我们还可以访问filter_var,我没有看到任何提及,因此以为我会把它扔在那里。要使用filter_var剥离<32和> 127的不可打印字符,可以执行以下操作:

过滤32位以下的ASCII字符

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

过滤127以上的ASCII字符

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

去除两者:

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

您还可以在去除高位时对低位字符(换行符,制表符等)进行html编码:

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

还有用于剥离HTML,清除电子邮件和URL等的选项。因此,存在许多用于清除(剥离数据)甚至进行验证的选项(如果无效则返回false,而不是静默剥离)。

消毒: http : //php.net/manual/en/filter.filters.sanitize.php

验证: http //php.net/manual/en/filter.filters.validate.php

但是,仍然存在问题,即FILTER_FLAG_STRIP_LOW会去除换行符和回车符,对于textarea来说,它们是完全有效的字符...因此,我想某些Regex答案有时还是有必要的,例如,在检查了此内容之后线程,我打算为textareas做到这一点:

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

这似乎比按数字范围删除的许多正则表达式更具可读性。



18

这更简单:

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


5
这还会去除换行符,回车符和UTF8字符。
大林

5
@Dalin没有“ UTF-8字符”之类的东西。有Unicode符号/字符,而UTF-8是可以代表所有符号的编码。您的意思是说这也会去除ASCII范围之外的字符
Mathias Bynens

1
吃掉阿拉伯字符:)
罗尔夫(Rolf)2013年

16

所有解决方案都部分起作用,甚至以下解决方案也无法涵盖所有​​情况。我的问题是试图在utf8 mysql表中插入字符串。字符串(及其字节)均符合utf8,但有几个错误序列。我认为其中大多数是控件或格式。

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

进一步加剧该问题的是表,服务器,连接,内容的呈现,如此处所讨论的


1
唯一通过我所有单元测试的测试,太棒了!
科里

\ xE2 \ x80 [\ xA4- \ xA8](或226.128。[164-168])-错误,该序列包含下一个可打印的符号:Unicode字符“ ONE DOT LEADER”(U + 2024),Unicode字符“ TWO DOT”领导者(U + 2025),Unicode字符'水平省略号(U + 2026),Unicode字符'HYPHENATION POINT'(U + 2027)。并且只有一个不可打印的:Unicode字符“ LINE SEPARATOR”(U + 2028)。下一个也是不可打印的:Unicode字符'PARAGRAPH SEPARATOR'(U + 2029)。因此,将序列替换为:\ xE2 \ x80 [\ xA8- \ xA9] \ xE2 \ x80 [\ xA8- \ xA9]以删除LINE SEPARATOR和PARAGRAPH SEPARATOR。
MingalevME

这是最好的解决方案,我能找到的,到目前为止,但我拉索不得不添加$s = preg_replace('/(\xF0\x9F[\x00-\xFF][\x00-\xFF])/', ' ', $s);,因为所有的表情符被搞乱了MySQL的
乔·布莱克

9

我的UTF-8兼容版本:

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


7
这样可以很好地删除引号,方括号等字符。这些字符肯定是可打印的字符。
Gajus 2014年

这太好了!它挽救了我的性命,在打印阿拉伯字符时陷入混乱,像冠军似的:)
克里希纳,2016年

6

您可以使用正则表达式删除希望保留的字符以外的所有内容:

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

替换所有非(^)字母AZ或az,数字0-9,空格,下划线,hypen,加号和&符-均不包含任何内容(即将其删除)。



4

从输入字符串中剥离所有非ASCII字符

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

该代码删除了十六进制范围0-31和128-255中的任何字符,只在结果字符串中保留了十六进制字符32-127,在本示例中,我将其称为$ result。


3

@PaulDixon答案完全错误的,因为它删除了可打印的 扩展ASCII字符 128-255!已经部分纠正。我不知道他为什么仍要从127个字符的7位ASCII集中删除128-255,因为它没有扩展的ASCII字符。

但最后重要的是不要删除128-255,因为例如chr(128)\x80)是8位ASCII 的欧元符号,而Windows中的许多UTF-8字体在我自己的测试中均显示欧元符号,而Android则显示 Android。

如果您从UTF-8字符串(可能是多字节UTF-8字符的起始字节)中删除ASCII字符128-255,它将杀死许多UTF-8字符。所以不要那样做!在所有当前使用的文件系统中,它们完全是合法字符。唯一保留的范围是0-31

而是使用此命令删除不可打印的字符0-31和127:

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

可用于ASCII和UTF-8,因为它们共享相同的控件集范围

不使用正则表达式的最快的慢速¹替代项:

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

如果你想保留的所有空格字符\t\n并且\r,然后删除chr(9)chr(10)chr(13)从该列表。注意:通常的空格是chr(32)这样,所以它留在结果中。确定自己是否要删除不间断的空间,chr(160)因为这会引起问题。

¹由@PaulDixon测试,并由我自己验证。


2

怎么样:

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

让我完全掌控要包含的内容


0

标记的anwser是完美的,但它错过了字符127(DEL),后者也是不可打印的字符

我的答案是

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

这个答案也是错误的。请参阅:stackoverflow.com/a/42058165/318765
mgutt

上面的答案是对原始答案的补充,它只添加了“删除”字符。
穆巴沙尔


0

对于仍在寻找如何做到这一点而不删除不可打印字符,而是转义它们的任何人,我提供了此帮助。随时进行改进!字符转义为\\ x [A-F0-9] [A-F0-9]。

像这样打电话:

$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

将正则表达式转换为Unicode的所选答案失败:0x1d(使用php 7.4)

一个办法:

<?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字符串删除除换行符以外的所有不可见字符

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.