从PHP字符串中删除控制字符

76

如何从PHP字符串中删除类似STX的控制字符?我已经尝试了一些方法

preg_replace("/[^a-zA-Z0-9 .\-_;!:?äÄöÖüÜß<>='\"]/","",$pString)

但我发现它删除了太多内容。是否有一种方法可以仅删除控制字符


以下链接可能有所帮助:<br/> ASCII字符表<br />POSIX参考<br />正则表达式 - Rohutech
6个回答

133
如果您所说的控制字符是指前32个ASCII字符和\x7F(包括回车等!),那么这将起作用:
preg_replace('/[\x00-\x1F\x7F]/', '', $input);

(Note the single quotes: with double quotes the use of \x00 causes a parse error, somehow.)
换行符和回车符(通常写为\r\n)可以通过以下方式避免被删除:
preg_replace('/[\x00-\x09\x0B\x0C\x0E-\x1F\x7F]/', '', $input);

我必须说,我认为Bobby的答案更好,因为[:cntrl:]更能传达代码的含义,而不是[\x00-\x1F\x7F]

警告:ereg_replace在PHP >= 5.3.0中已被弃用,并在PHP >= 7.0.0中删除!请使用preg_replace代替ereg_replace

preg_replace('/[[:cntrl:]]/', '', $input);

7
不幸的是,ereg_replace在PHP 5.3中已经被弃用,而mb版本比preg_replace慢。使用preg_replace有一种稍微更加简洁的方法,在我的测试中,它比上面那个方法略微快一些(处理数十万项时快1%):preg_replace('/[\p{Cc}]/', '', $input); - Jay Paroline
9
另外,preg_replace('/[[:cntrl:]]/', '', $input); 对我来说非常有效(php 5.2.6)。 - ford
对于我来说不起作用,这个字符串>>“Rua Enette Dubard, 806 - Loja 2”被转换为>>“Rua Eee Dubad,806 - Loja 2”,并且回车字符仍然存在。 - ruhalde
2
请注意,您可能还想保存制表符“\t”。我发现这个问题是因为我的数据库中出现了“\x1D”。 - jcampbell1
请查看以下链接,了解 preg_replace('/[[:cntrl:]]/', '', $input) 的原因:https://dev59.com/snRB5IYBdhLWcg3w6LN2 - David
对于清理控制台输入,第一个 preg_replace 起作用了,但第二个没有(我认为第二个只是第一个的扩展)。 - myol

49

对于Unicode输入,这将从您的输入文本中删除所有控制字符、未分配、私有使用、格式和代理代码点(如果它们不是空格字符,比如制表符、换行符)。我使用此方法从我的输入中删除所有不可打印字符。

<?php
$clean = preg_replace('/[^\PC\s]/u', '', $input);

了解有关 \p{C} 的更多信息请参见http://www.regular-expressions.info/unicode.html#category


为什么你使用\PC而不是\p{C} - syl.fabre
1
这正是在向Authorize.net API发送用户输入时所需的。以防其他人遇到无效的XML字符错误。 - Nostalg.io
能否以白痴的话(是的,我就是)向我解释一下这个是如何工作的?它确实可以工作,我知道我已经使用了广泛的单元测试覆盖,但是当我根据我的当前理解重新阅读它时,它就不合理了。我理解的方式是,它看起来应该将除控制字符或空格之外的任何内容替换为无,也就是说,最终只剩下控制字符和空格...?提前感谢! - Chris Rosillo
2
嗨@ChrisRosillo,我们使用\PC的反向形式来匹配控制字符。因此,当\p{C}匹配控制字符时,\PC匹配任何不是控制字符的内容。然后,我们使用否定字符类[^..]来表示匹配/替换任何“不是[不是控制字符或空格]”的内容。因此,这有点像双重否定。 - Scott Jungwirth
1
@syl.fabre 关于括号的说明:“如果只使用 \p 或 \P 指定一个字母,则它包含所有以该字母开头的属性。在这种情况下,在没有否定的情况下,转义序列中的花括号是可选的。” - pmiguelpinto
显示剩余2条评论

24

PHP支持POSIX类,因此您可以使用[:cntrl:]而不是一些花哨的字符魔法:

ereg_replace("[:cntrl:]", "", $pString);

编辑:

在5.3版本中可能需要额外的一对方括号。

ereg_replace("[[:cntrl:]]", "", $pString);

1
PHP确实支持POSIX,使用ereg函数而不是preg:http://nl2.php.net/manual/en/book.regex.php - Duroth
在我的测试中,只有在语句中添加额外的方括号时才能正常工作,就像这样:ereg_replace("[[:cntrl:]]", "", $pString); PHP 5.3.5。 - dereferenced
2
由于 PHP 7.0 中已经移除了 ereg_replace,因此在 PHP > 7.0 中应该使用 preg_replace("/[[:cntrl:]]/", "", $input); - wowpatrick

12

简短回答

使用这个正则表达式...

/[^\PCc^\PCn^\PCs]/u

像这样...

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

TLDR Explanation

  • ^\PCc : Do not match control characters.
  • ^\PCn : Do not match unassigned characters.
  • ^\PCs : Do not match UTF-8-invalid characters.

Working Demo

Simple demo to demonstrate: IDEOne Demo

$text = "\u{0019}hello";
print($text . "\n\n");
$text = preg_replace('/[^\PCc^\PCn^\PCs]/u', '', $text);
print($text);

输出:

(-Broken-Character)hello
hello

备选项

  • ^\PC:仅匹配可见字符。 不匹配任何不可见字符。
  • ^\PCc:仅匹配非控制字符。 不匹配任何控制字符。
  • ^\PCc^\PCn:仅匹配已分配的非控制字符。 不匹配任何控制或未分配字符。
  • ^\PCc^\PCn^\PCs:仅匹配已分配且UTF-8有效的非控制字符。 不匹配任何控制、未分配或UTF-8无效字符。
  • ^\PCc^\PCn^\PCs^\PCf:仅匹配已分配且UTF-8有效的非控制、非格式化字符。 不匹配任何控制、未分配、格式化或UTF-8无效字符。

来源和解释

查看可以在正则表达式中使用的Unicode字符属性。 您应该能够在Microsoft .NETJavaScriptPythonJavaPHPRubyPerlGolang,甚至Adobe中使用这些正则表达式。 知道Unicode字符类是非常易于转移的知识,因此我建议使用它!

给定其简写和全写形式,此正则表达式将匹配任何可见字符。

\PL\PM\PN\PP\PS\PZ
\PLetter\PMark\PNumber\PPunctuation\PSymbol\PSeparator

通常,\p表示我们想要匹配的内容,而我们使用\P(大写)表示不匹配的内容。但是PHP没有这个功能,所以我们需要在正则表达式中使用^来进行手动否定。
一个更简单的正则表达式是^\PC,但这可能在删除不可见格式时过于严格。您可能需要仔细查看并确定哪种替代方案最适合您的需求。

所有可匹配的Unicode字符集

如果您想了解其他可用的字符集,请查看regular-expressions.info...
  • \PL\PLetter:来自任何语言的任何字母。
    • \PLl\PLowercase_Letter:具有大写变体的小写字母。
    • \PLu\PUppercase_Letter:具有小写变体的大写字母。
    • \PLt\PTitlecase_Letter:仅当单词的第一个字母大写时,出现在单词开头的字母。
    • \PL&\PCased_Letter:存在小写和大写变体的字母(Ll、Lu和Lt的组合)。
    • \PLm\PModifier_Letter:像字母一样使用的特殊字符。
    • \PLo\POther_Letter:没有小写和大写字母的字母或表意文字
  • \PM\PMark:旨在与另一个字符组合的字符(例如重音符号、umlauts、封闭框等)。
    • \PMn\PNon_Spacing_Mark:旨在与另一个字符组合而不占用额外空间的字符(例如重音符号、umlauts等)。
    • \PMc\PSpacing_Combining_Mark:旨在与占用额外空间的另一个字符组合的字符(许多东方语言中的元音标志)。
    • \PMe\PEnclosing_Mark:将其与其他字符组合的字符(圆形、正方形、按键等)。
  • \PZ\PSeparator:任何类型的空格或不可见分隔符。
    • \PZs\PSpace_Separator:不可见的空格字符,但会占用空间。
    • \PZl\PLine_Separator:行分隔符字符U+2028。
    • \PZp\PParagraph_Separator:段落分隔符字符U+2029。
  • \PS\PSymbol:数学符号、货币符号、dingbats、盒式字符等。
    • \PSm\PMath_Symbol:任何数学符号。
    • \PSc\PCurrency_Symbol:任何货币符号。
    • \PSk\PModifier_Symbol:作为自己的完整字符的组合字符(标记)。
    • \PSo\POther_Symbol:各种不是数学符号、货币符号或组合字符的符号。
  • \PN\PNumber:任何脚本中的任何类型的数字字符。
    • \PNd\PDecimal_Digit_Number:除表意文字脚本外的任何脚本中的数字零至九。
    • \PNl

6
为了保留控制字符并使它们兼容于JSON,我必须执行以下操作:
$str = preg_replace(
    array(
        '/\x00/', '/\x01/', '/\x02/', '/\x03/', '/\x04/',
        '/\x05/', '/\x06/', '/\x07/', '/\x08/', '/\x09/', '/\x0A/',
        '/\x0B/','/\x0C/','/\x0D/', '/\x0E/', '/\x0F/', '/\x10/', '/\x11/',
        '/\x12/','/\x13/','/\x14/','/\x15/', '/\x16/', '/\x17/', '/\x18/',
        '/\x19/','/\x1A/','/\x1B/','/\x1C/','/\x1D/', '/\x1E/', '/\x1F/'
    ), 
    array(
        "\u0000", "\u0001", "\u0002", "\u0003", "\u0004",
        "\u0005", "\u0006", "\u0007", "\u0008", "\u0009", "\u000A",
        "\u000B", "\u000C", "\u000D", "\u000E", "\u000F", "\u0010", "\u0011",
        "\u0012", "\u0013", "\u0014", "\u0015", "\u0016", "\u0017", "\u0018",
        "\u0019", "\u001A", "\u001B", "\u001C", "\u001D", "\u001E", "\u001F"
    ), 
    $str
);

JSON规则说明:“除了必须转义的引号、反斜杠和控制字符(U+0000到U+001F),所有Unicode字符都可以放在引号内。”

1

正则表达式免费方法

如果您只想过滤掉我熟悉的控制字符(即32和127以下的字符),请尝试以下方法:

 for($control = 0; $control < 32; $control++) {
     $pString = str_replace(chr($control), "", $pString;
 }

$pString = str_replace(chr(127), "", $pString;

循环将除DEL之外的所有内容都删除,然后我们只需将其添加到末尾。

我认为这比处理正则表达式和正则表达式库要轻松得多。

更新的无正则表达式方法

仅仅是为了好玩,我想出了另一种方法来实现它。这个方法使用一个控制字符数组:

$ctrls = range(chr(0), chr(31));
$ctrls[] = chr(127);

$clean_string = str_replace($ctrls, "", $string);

1
这样做与 ereg_replace("[:cntrl:]", "", $pString) 相比,如何才能更少地 "有压力"?使用 ereg,PHP 解释器可能会编译出比使用那个循环更高效的中间代码。 - glomad
6
ereg_replace 自 PHP 5.3.0 起已被弃用。 - Wiliam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接