用于查找URL的正则表达式,可以改进吗?

时间:2011-12-15 13:48:12

标签: php regex

作为一名编码经验有限的设计师,我一直认为正则表达式是某种黑魔法。最近,我一直在阅读 - 我对它的可能性非常感兴趣。所以我决定在我目前的php项目中首次尝试。

我想找到以下结构的所有网址:

http://[any subdomain, only a-z].domain.com/[any subfolder, can contain a-z,A-Z,0-9,- and _]/

示例:

我的正则表达式:

http://[a-z]*\.domain\.com/[A-Za-z0-9\_\-]*/

我的问题:

  • 正则表达式正在运行,但我只是想知道它是否可以改进。例如,我尝试使用(i?)添加case insencitive,但无法使其正常工作。
  • 如果我在表达式的开头和结尾添加双引号,我只能在php中使用它,为什么会这样? $ref = preg_replace('"http://[a-z]*\.domain\.com/[A-Za-z0-9\_\-]*/"','',$ref);

5 个答案:

答案 0 :(得分:3)

在php中,正则表达式必须由/分隔,但它几乎可以是任何字符。

您的第二次尝试有效的原因是您使用"作为分隔符。

要区分大小写,您必须将标记i放在第二个分隔符之后:

$ref = preg_replace('"http://[a-z]*\.domain\.com/[A-Za-z0-9\_\-]*/"i','',$ref);
                                                           here ___^

使用i标记,不需要[a-zA-Z][a-z]就足够了。此外,如果它位于字符类中的第一个或最后一个位置,则不需要转义字符类中的下划线_而不需要转义符号-

$ref = preg_replace('"http://[a-z]*\.domain\.com/[a-z0-9_-]*/"i','',$ref);

请注意,[a-zA-Z0-9_]可以缩写为\w,然后您的代码可能如下所示:

$ref = preg_replace('"http://[a-z]*\.domain\.com/[\w-]*/"i','',$ref);

考虑到*代表0次或更多次,因此您的正则表达式将匹配以下内容:

http://.domain.com//

*更改为+,这意味着一次或多次确保子域至少有一个字符,子文件夹有一个字符:

$ref = preg_replace('"http://[a-z]+\.domain\.com/[\w-]+/"i','',$ref);

然后"对于分隔符不常见,例如使用#~!

$ref = preg_replace('#http://[a-z]+\.domain\.com/[\w-]+/#i','',$ref);

答案 1 :(得分:3)

以相反的顺序回答您的问题。

  

如果我在表达式的开头和结尾添加双引号,我只能在php中使用它,为什么会这样? $ ref = preg_replace('http:// [az] .domain.com / [A-Za-z0-9 _-] /“','',$ ref);

您的双引号充当正则表达式分隔符。通常情况下,正斜杠会扮演这个角色,使用它们实际上要求你像对待点一样逃脱它们。几乎任何标点符号都可以作为您的分隔符,因此以下内容都是等效的:

$pattern0 = '"http://[a-z]*\.domain\.com/[A-Za-z0-9\_\-]*/"';
$pattern1 = '!http://[a-z]*\.domain\.com/[A-Za-z0-9\_\-]*/!';
$pattern2 = '/http:\/\/[a-z]*\.domain\.com\/[A-Za-z0-9\_\-]*\//';

这些都是完全有效的,但如果!不清楚,则惯例是使用/作为分隔符。我将在下一个问题中坚持使用$pattern1

  

正则表达式正在运行,但我只是想知道它是否可以改进。例如,我尝试使用(i?)添加case insencitive,但无法使其工作。

在最终的正则表达式分隔符之后添加i以防止不区分大小写:

'!http://[a-z]*\.domain\.com/[A-Za-z0-9\_\-]*/!i'

将连字符-移动到角色范围的开头,这样你就不会逃脱它。此外,没有必要逃避下划线:

'!http://[a-z]*\.domain\.com/[-A-Za-z0-9_]*/!i'

接下来,使用字符类来简化字符范围。在这种情况下,\w[a-zA-Z0-9_]匹配。

'!http://[a-z]*\.domain\.com/[-\w]*/!i'

最后,您使用*作为量词意味着您可能会获得一些奇怪的,最终无效的匹配。所有这些都匹配:

  

http://www.domain.com/foo/
  http://.domain.com/foo/
  http://.domain.com//

最后两个被打破了。如果您正在解析已知的好URL(例如来自日志文件),那不是真正的问题。如果您需要更严格,请使用+量词来要求子域和路径中至少有一个字符:

'!http://[a-z]+\.domain\.com/[-\w]+/!i'

现在有点可读了。

答案 2 :(得分:1)

如果您在HTTP之后有[a-z]*\.,那么这将匹配http://.domain.com/etc/,这是无效的。我建议改为

http://([a-z]+\.)?domain\.com/[A-Za-z0-9\_\-]*/

这将匹配http://domain.com/etc/http://www.domain.com/etc/,但不匹配http://.domain.com/etc/

如果您必须拥有子域名,我建议使用http://[a-z]+\.domain\.com/[A-Za-z0-9\_\-]*/,这会强制子域名存在。

与最后一组相似。目前它将允许http://www.domain.com//。我建议使用+代替*强制存在目录名称,即[A-Za-z0-9\_\-]+

答案 3 :(得分:1)

之前的 M42 提供了一个很好的答案。我只想补充两点:

  • 我会用“https?”相反,如果允许https链接
  • 我会添加一个?在最后一次斜线之后(因为它可能会丢失并且大多数时候意味着同样的事情)

所以模式会是这样的:

$pattern = '/https?\:\/\/[a-z]+\.domain\.com\/[a-z0-9\_\-]*\/?/i';

答案 4 :(得分:0)

  

正则表达式正在运行,但我只是想知道它是否可以   改进。       例如,我尝试使用(i?)添加case insencitive,但无法使其工作。

尝试使用i flag

  

如果我在开始时添加双引号,我只能在php中使用它   并且结束了表达,为什么会这样?

这是因为你的正则表达式"delimiter