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

回答 17 浏览 27.9万 2009-07-24

我想我需要删除0-31和127这两个字符。

是否有一个函数或一段代码可以有效地做到这一点?

Stewart Robinson 提问于2009-07-24
17 个回答
#1楼 已采纳
得票数 437

7 bit ASCII?

如果你的Tardis刚刚降落在1963年,而你只想要7位可打印的ASCII字符,你可以用这个撕掉0-31和127-255之间的所有内容。

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

它匹配范围为0-31、127-255的任何东西,并将其删除。

8 bit extended 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,有潜在的许多非打印元素,但让我们考虑一个简单的元素。无破绽空格(U+00A0)

在一个UTF-8字符串中,这将被编码为0xC2A0。你可以寻找并删除这个特定的序列,但由于/u修改器的存在,你可以简单地将\xA0添加到字符类中:

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

Addendum: What about 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有微小的优势。

我认为这是一个有趣的结果,所以在这里包括它。 重要的不是把这个结果拿来用它来决定使用哪种方法,而是对照自己的数据进行基准测试,然后再决定。

Paul Dixon 提问于2009-07-24
Community 修改于2017-05-23
如果你需要考虑换行安全,可以将表达式改为这样(反过来搜索可打印的内容):preg_replace(/[^\x0A\x20-\x7E]/,'',$string)。Nick 2010-09-16
@Dalin 不存在 "UTF-8字符 "这样的东西。有一些Unicode符号/字符,而UTF-8是一种可以代表所有这些符号的编码。你的意思是说这对ASCII字符集以外的字符不起作用。Mathias Bynens 2012-12-31
如果你需要匹配一个高于\xFF的unicode字符,请使用\x{####}。Peter Olson 2013-07-10
这将删除阿拉伯字母,是个坏办法。Ayman Hussein 2014-03-27
是一种编码,而不是一个字符。上面的解决方案只适用于ASCII字符。Paul Dixon 2015-05-21
#2楼
得票数 157

这里的许多其他答案没有考虑到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。

Dalin 提问于2011-11-17
rybo111 修改于2022-09-08
你的regexp处理UTF8字符很好;但它剥离了非UTF8的 "特殊 "字符;如ç, ü 和ö。'/[\x00-\x1F\x80-\xC0]/u'保留了它们;但也保留了除法(F7)和乘法(D7)符号。Hazard 2012-05-09
@Hazar 是的,你是正确的,\x80-\xFF剥离了太多,但\x80-\xC0仍然太过限制。这将错过其他可打印的字符,如©£±。参考资料见utf8-chartable.deDalin 2013-02-07
@TimMalone 因为PHP会扩展这些字符序列。php.net/manual/en/…,所以搜索引擎不会看到你想告诉它的范围。Dalin 2016-10-20
那7F呢?它不应该是\x7F-\x9F吗?Bell 2016-11-23
我试了很多,我试了PHP中所有的编码功能,从regex到mb_到htmlspecialchars等等。没有任何东西可以去除控制字符,感谢你的投资工作。John 2018-01-06
#3楼
得票数 44

从 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将剥离换行和回车,而这对于文本区域来说是完全有效的字符......因此,我想,一些Regex答案有时仍然是必要的,例如,在回顾了这个主题之后,我计划对文本区域进行这样的处理。

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

这似乎比一些按数字范围剥离的勒格函数更容易阅读。

Kevin Nelson 提问于2015-03-10
Kevin Nelson 修改于2015-03-10
#4楼
得票数 27

你可以使用字符类

/[[:cntrl:]]+/
ghostdog74 提问于2009-07-24
这难道不需要我使用ereg吗?Stewart Robinson 2009-07-24
#5楼
得票数 22

所有的解决方案都有部分作用,即使是下面的解决方案也可能没有涵盖所有的情况。我的问题是试图将一个字符串插入到一个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;
}

使问题进一步恶化的是表与服务器与连接与内容的渲染,正如这里谈到的一点

Wayne Weibel 提问于2013-12-24
Community 修改于2017-05-23
唯一一个通过我所有单元测试的,真棒!"他说。Korri 2016-04-08
\xE2\x80[\xA4-\xA8](或226.128.[164-168])--是错误的,该序列包括下一个可打印符号。Unicode字符 "ONE DOT LEADER"(U+2024),Unicode字符 "TWO DOT LEADER"(U+2025),Unicode字符 "HORIZONTAL ELLIPSIS"(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]以删除 "行分隔符 "和 "段落分隔符"。MingalevME 2018-03-07
这是我目前能找到的最好的解决方案,但我不得不添加$s = preg_replace('/(\xF0\x9F[\x00-\xFF][\x00-\xFF])/', ' ', $s);,因为所有的emoji字符都扰乱了mysql的运行。Joe Black 2019-05-18
不幸的是,上面的 "糟糕的utf-8 "Regex还删除了换行符!这是不可能的。Avatar 2022-03-10
#6楼
得票数 17

这更简单。

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

jacktrade 提问于2011-04-20
jacktrade 修改于2017-02-23
这也是对换行、回车和UTF8字符的剥离。Dalin 2011-12-17
@Dalin 没有所谓的 "UTF-8字符"。有Unicode符号/字符,而UTF-8是一个可以代表所有这些符号的编码。你的意思是说这把ASCII范围之外的字符也剥离出来Mathias Bynens 2012-12-31
吃了阿拉伯文的字符 :)Rolf 2013-06-26
#7楼
得票数 13

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

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

这段代码删除了十六进制范围内0-31和128-255的任何字符,在产生的字符串中只留下十六进制字符32-127,在这个例子中我把它称为$result。

Junaid Masood 提问于2019-01-07
为什么我想要127,也就是DEL?作为[\x00-\x1F\x7F-\xFF],删除127到255而不是128到255不是更好吗?Volomike 2022-02-09
#8楼
得票数 13

对于UTF-8,请试着这样做。

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

这是我10年前的原始答案,正如评论中所说的那样,这很适合于为全文搜索引擎提供信息,因为它删除了一些非文本可打印的字符,如[]!~等等。

如果你还需要删除无效的字符,比如说,喂食libexpat(叹气),你可以尝试:

preg_replace('/[^\PCc^\PCn^\PCs]/u', '', $string);

请参阅本答案,以了解更多关于该方法的信息。

cedivad 提问于2012-05-06
cedivad 修改于2022-01-25
这很好地删除了像引号、括号等字符。这些当然是可打印的字符。Gajus 2014-01-27
这真是太好了!它救了我的命,在打印阿拉伯字符时出了乱子,它像冠军一样工作:)krishna 2016-05-26
当只需要纯粹的词语时,这可能是有用的。例如,对于页面上的搜索引擎和数据库中的索引。这样,括号、句号和逗号就没有必要了。Robert 2021-10-15
#9楼
得票数 10

你可以使用正则表达式来删除除你希望保留的那些字符之外的所有内容。

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

将所有不是(^)的字母A-Z或a-z、数字0-9、空格、下划线、连字符、加号和安培号--替换为空(即删除它)。

Richy B. 提问于2009-07-24
#10楼
得票数 6
preg_replace('/(?!\n)[\p{Cc}]/', '', $response);

这将删除所有控制字符 (http://uk. php.net/manual/en/regexp.reference.unicode.php)留下\n 换行符。根据我的经验,控制字符是最常导致打印问题的字符。

Gajus 提问于2013-03-01
它对我来说是完美的!我只为UTF-8字符添加了/u。能否请你解释一下第一部分(?!\n)的作用?Marcio Mazzucato 2017-05-15
完美!我正在寻找一种方法来删除unicode的 "无用 "字符,并保留重要的字符(包括重音字母、数字、特殊字符)。谢谢你的回答和文档链接azerto00 2020-10-26
#11楼
得票数 5

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

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

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

相反,使用这个方法来删除不可打印的字符0-31和127。

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

适用于 ASCII 和 UTF-8,因为它们共享 同控制集范围.

不使用正则表达式的最快速的slower¹替代方案。

$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测试,并由我自己验证。

mgutt 提问于2017-02-05
Community 修改于2017-05-23
#12楼
得票数 2

对Unicode来说,将regex输入到选定的答案是失败的。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 字符串删除除换行外的所有不可见字符

Mkdgs 提问于2020-06-12
#13楼
得票数 1

如何。

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

使我能够完全控制我想要包括的内容

sdfor 提问于2014-04-11
George Brighton 修改于2015-04-08
#14楼
得票数 1

对于那些仍在寻找如何在不删除不可打印的字符的情况下做到这一点,而是将其转义,我做了这个来帮助大家。请自由地改进它!字符被转义为:[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));
    }
?>
DropItLikeItsHot 提问于2017-12-28
#15楼
得票数 0

标记的答案是完美的,但它漏掉了127(DEL)字符,这也是一个不可打印的字符。

我的回答是

$string = preg_replace('/[\x00-\x1F\x7f-\xFF]/', '', $string);
Mubashar 提问于2013-08-08
这个答案也是错误的。请看。stackoverflow.com/a/42058165/318765mgutt 2017-02-06
上面的答案是对原始答案的赞美,它只增加了 "删除 "字样。Mubashar 2020-03-16
#16楼
得票数 0

"cedivad "为我解决了这个问题,瑞典字母ÅÄÖ的结果是持续的。

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

谢谢!

Andreas Ek 提问于2015-03-14
#17楼
得票数 -1

我使用https://github.com/neitanod/forceutf8,解决了UTF8的问题。

use ForceUTF8\Encoding;

$string = Encoding::fixUTF8($string);
Nick 提问于2018-07-03
这个库将UTF-8重音字符和UTF-8表情符号转换为"?"符号。不幸的是,这个问题相当严重。ChristoKiwi 2018-09-17
标签