184

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

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

4

17 回答 17

419

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 非常有效,但是如果您经常执行此操作,则可以构建一个要删除的字符数组,并使用 str_replace,如下面的 mgutt 所述,例如

//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 有一个边缘。

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

于 2009-07-24T10:51:46.083 回答
150

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

于 2011-11-17T17:50:00.223 回答
40

从 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 将去除换行符和回车符,这对于文本区域来说是完全有效的字符......所以我想,一些正则表达式的答案有时仍然是必要的,例如在审查这个之后线程,我计划为 textareas 执行此操作:

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

这似乎比一些被数字范围剥离的正则表达式更具可读性。

于 2015-03-10T18:02:12.370 回答
27

你可以使用字符类

/[[:cntrl:]]+/
于 2009-07-24T10:57:16.417 回答
18

所有的解决方案都部分地起作用,甚至下面可能并不能涵盖所有的情况。我的问题是尝试将字符串插入 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;
}

进一步加剧问题的是表格与服务器与连接与内容的渲染,正如这里所讨论的那样

于 2013-12-24T20:52:47.973 回答
17

这更简单:

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

于 2011-04-20T09:40:47.450 回答
11

对于 UTF-8,试试这个:

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

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

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

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

有关该方法的更多信息,请参阅此答案

于 2012-05-06T12:56:42.627 回答
10

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

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

替换所有不是 (^) 字母 AZ 或 az、数字 0-9、空格、下划线、连字符、加号和 & 符号的内容(即删除它)。

于 2009-07-24T10:50:59.253 回答
9

从输入字符串中去除所有非 ASCII 字符

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

该代码删除了十六进制范围 0-31 和 128-255 中的所有字符,只在结果字符串中留下十六进制字符 32-127,在本例中我称之为 $result。

于 2019-01-07T09:56:32.093 回答
6
preg_replace('/(?!\n)[\p{Cc}]/', '', $response);

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

于 2013-03-01T11:06:52.233 回答
5

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

但最后重要的是不要删除 128-255,因为例如chr(128)( \x80) 是 8 位 ASCII 中的欧元符号,Windows 中的许多 UTF-8 字体显示欧元符号,而 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 测试并由我自己验证。

于 2017-02-05T22:41:31.197 回答
1

怎么样:

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

让我完全控制我想要包含的内容

于 2014-04-11T04:05:54.427 回答
1

对于仍在寻找如何在不删除不可打印字符而是转义它们的情况下如何执行此操作的任何人,我这样做是为了提供帮助。随意改进它!字符转义为 \\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));
    }
?>
于 2017-12-28T18:22:14.780 回答
0

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

我的回答是

$string = preg_replace('/[\x00-\x1F\x7f-\xFF]/', '', $string);
于 2013-08-08T03:54:49.557 回答
0

“cedivad”通过瑞典字符 ÅÄÖ 的持续结果为我解决了这个问题。

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

谢谢!

于 2015-03-14T12:07:11.893 回答
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 String 删除除换行符以外的所有不可见字符

于 2020-06-12T10:51:59.207 回答
-1

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

use ForceUTF8\Encoding;

$string = Encoding::fixUTF8($string);
于 2018-07-03T08:55:51.060 回答