PHP中更高效的字符串清理正则表达式

4

好的,我希望有人能帮我一下正则表达式。

我正在尝试清理一个字符串。

基本上,我正在做以下几件事:

  1. 用替换字符替换除A-Za-z0-9之外的所有字符。

  2. 将替换字符的连续重复项替换为单个替换字符。

  3. 从字符串开头和结尾修剪替换字符。

示例输入:

(&&(%()$()#&#&%&%%(%$+-_The dog jumped over the log*(&)$%&)#)@#%&)&^)@#)

所需输出:

The+dog+jumped+over+the+log

我目前正在使用这个非常混乱的代码,并且知道有更优雅的方法来完成这个任务....

function clean($string, $replace){

    $ok = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    $ok .= $replace;
    $pattern = "/[^".preg_quote($ok, "/")."]/";

    return trim(preg_replace('/'.preg_quote($replace.$replace).'+/', $replace, preg_replace($pattern, $replace, $string)),$replace);
}

能否请一位正则表达式大师给我提供一个更简单/更有效的解决方案?


Botond Balázs和hakre提出并解释了一个更好的解决方案:

function clean($string, $replace, $skip=""){
    // Escape $skip
    $escaped = preg_quote($replace.$skip, "/");

    // Regex pattern
    // Replace all consecutive occurrences of "Not OK" 
    // characters with the replacement
    $pattern = '/[^A-Za-z0-9'.$escaped.']+/';

    // Execute the regex
    $result = preg_replace($pattern, $replace, $string);

    // Trim and return the result
    return trim($result, $replace);
}

1
对于1和2,您可以尝试使用替换来替换“[^A-Za-z0-9]+”。 - Botond Balázs
3
我讨厌 Stack Overflow 迫使我选择一个答案... - Samantha P
1
我认为将模式放在单独的变量中的版本更易读。 - Botond Balázs
1
我选择了Botond Balázs的答案作为代码示例。但是我想向hakre表示拥抱/感谢他的深入解释和帮助。谢谢大家! - Samantha P
2个回答

2

我不是一个“正则表达式高手”,但以下是我的做法。

function clean($string, $replace){
    /// Remove all "not OK" characters from the beginning and the end:
    $result = preg_replace('/^[^A-Za-z0-9]+/', '', $string);
    $result = preg_replace('/[^A-Za-z0-9]+$/', '', $result);

    // Replace all consecutive occurrences of "not OK" 
    // characters with the replacement:
    $result = preg_replace('/[^A-Za-z0-9]+/', $replace, $result);

    return $result;
}

我想这部分内容可以更简单些,但在处理正则表达式时,清晰易读通常比聪明或编写超级优化的代码更重要。
让我们看看它是如何工作的:
/ ^ [^ A-Za-z0-9] + /: ^ 匹配字符串开头。 [ ^ A-Za-z0-9 ] 匹配所有非字母数字字符。 + 表示“匹配前一个字符一次或多次”。
/ [^ A-Za-z0-9] + $ /: 与上述相同,只是 $ 匹配字符串结尾。
/ [^ A-Za-z0-9] + /: 与上述相同,只是它也匹配字符串中间。
编辑:OP 是正确的,前两个可以替换为调用 trim():
function clean($string, $replace){
    // Replace all consecutive occurrences of "not OK" 
    // characters with the replacement:
    $result = preg_replace('/[^A-Za-z0-9]+/', $replace, $result);

    return trim($result, $replace);
}

2

我不想听起来过于聪明,但我不会称它为regex-foo。

你所做的事情实际上基本上是正确的,因为你使用了preg_quote,而许多其他人甚至没有意识到该函数的存在。

然而,可能是在错误的位置。错误的位置是因为你引用了字符类中的字符,并且在正则表达式中有(类似但)不同的引用规则。

此外,正则表达式已经考虑到了像你这样的情况。这可能是你寻找向导的部分,让我们看看如何使你的负字符类更加紧凑(我将生成代码省略以使其更加清晰):

[^0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]

有一些结构,比如0-9A-Za-z可以准确地表示它们。你可以看到-是字符类内的一个特殊字符,它不是字面上的意思,而是代表从某个字符到另一个字符的范围:

[^0-9A-Za-z]

这样就更加紧凑并且表示相同。还有像\d\w这样的符号,可能在您的情况下会很方便。但是暂时使用第一种变体,因为我认为它已经非常明显了。

另一个部分是重复。我们看到有+,意思是一个或多个。所以您想替换一个或多个不匹配的字符。通过在应匹配的部分末尾添加+来使用它(默认情况下它是贪婪的,所以如果有5个字符,那么将取这5个字符而不是4个):

[^0-9A-Za-z]+

我希望这对你有所帮助。接下来的一个步骤则是只需要去掉开头和结尾不匹配的字符,但现在还早,我对此并不熟练。


2
很好的解释。对于提问者,我建议阅读《精通正则表达式》这本书。它让我大开眼界。 - Botond Balázs
1
@BotondBalázs:非常正确。作为在线资源,我也觉得http://www.regular-expressions.info/不错。即使是正则表达式的PHP手册现在也有所改进,过去还有点简略:http://www.php.net/manual/en/pcre.pattern.php - hakre
1
作为 RegexBuddy 的在线(免费)替代品,我推荐 http://gskinner.com/RegExr/ - 尽管在功能方面没有什么能比得上 RegexBuddy :) - Botond Balázs
确实,非常有帮助和详尽的回答。谢谢您。关于您对 preg_quote 的评论,我需要使用它,因为在我的问题中没有提到,我必须能够即时添加可能是语法的“好”的字符。在哪里和如何适当地使用 preg_quote 转义呢? - Samantha P
正如所写的那样,了解并利用preg_quote是很好的。我认为它不会给你带来任何问题,只是想指出,在某些边缘情况下,它可能无法完全引用所需的内容。但这并不意味着它会引入问题。 - hakre
显示剩余2条评论

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