使用Perl正则表达式确定URI是否有效

时间:2011-06-23 03:32:41

标签: regex perl escaping

对于我正在开发的应用程序,我需要一个Perl脚本,它循环遍历一个大量的CSV文件,并确保每一行包含一个有效的URI。我之前已经问过一个关于解析CSV文件的问题,我已经开始使用Text::CSV来让我的生活更轻松。现在我遇到了确保URI有效的问题。

由于我的应用程序的性质,URI不需要采用完整形式的

protocol://username:password@domain.extension/request?vars=values

相反,我只对此请求部分感兴趣。对于一般网站,这可以是.com.edu等之后的任何内容。

我目前有以下Perl脚本:

if($_ !~ /^(?:[a-z0-9-._~!$&'()*+,;=:/?@]|%[0-9A-F]{2})*$/i){
    print "Invalid URL format";
    exit;
} else {
    /* stuff */
}

正则表达式应该是相当直接的。允许请求包含一小组符号([a-z0-9-._~!$&'()*+,;=:/?@])中的一个,或者它可以包含百分号(%),后跟两个十六进制数字。这些模式中的任何一个都可以无限重复。

当我运行此脚本时,我收到以下错误:

Number found where operator expected at ./301rules.pl line 58, near "%[0"
        (Missing operator before 0?)
Bareword found where operator expected at ./301rules.pl line 58, near "9A"
        (Missing operator before A?)
Bareword found where operator expected at ./301rules.pl line 58, near "$/i"
        (Missing operator before i?)
syntax error at ./301rules.pl line 58, near "%[0"

很明显,我的正则表达式中的某些内容需要被转义,但我不确定是什么。我尝试转义每个可能的符号来创建以下正则表达式:

if($_ !~ /^(?:[a-z0-9\-\.\_\~\!\$\&\'\(\)\*\+\,\;\=\:\/\?\@]|%[0-9A-F]{2})*$/i){

然而,当我这样做时,它只允许每个字符串通过测试,即使是我知道无效的字符串,例如te%sté

那么有没有人有Perl正则表达式的经验,知道我需要逃避什么以及我不应该逃避什么?有了19个不同的符号,我不想尝试所有2 ^ 19 = 524288的可能性。

编辑 - 投票结束。我发现问题实际上存在于此循环之上,尽管我还不完全理解为什么。

我有:

if( $_ == "" ){
    next;
}
/* regex conditional from above */

无论出于何种原因,尽管明确存储在$_中的数据,但它仍然继续评估为真并进入下一次迭代。我会弄清楚为什么会这样,但是现在这个正则表达式可以正常运行。

3 个答案:

答案 0 :(得分:5)

URI module的文档中,我发现了以下内容:

  

使用REGEXP PARSING URI

     

作为这个模块的替代品,   以下(官方)定期   表达式可用于解码a   URI:

    my($scheme, $authority, $path,
    $query, $fragment) =   $uri =~
    m|(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?|;
     

URI :: Split模块提供了   函数uri_split()作为可读的替代方案。

但我认为Regexp::Common::URI可能是HTTP URI语法验证的理想解决方案。

use Regexp::Common qw /URI/;
while (<>) {
    /$RE{URI}{HTTP}/  and  print "Contains an HTTP URI.\n";
}

任何由达米安写的并由阿比盖尔维护的东西都必须受到启发,伟大,疯狂或以上所有。 (我的意思是尽可能高的。)

答案 1 :(得分:2)

我不知道你是如何得到你的第一个正则表达式,但我会尽力帮助你解决这个问题。你只需要逃避正则表达式中具有特殊含义的字符 - 从你的正则表达式,它们是: - ,。,$,(,),*,/,所以正则表达式应该是这样的:

if($_ !~ /^(?:[a-z0-9\-\._~!\$&'\(\)\*+,;=:\/?@]|%[0-9A-F]{2})*$/i){

我并不完全知道?:试图在那里实现什么,但是你的第一个字符类(第一个[]之间的表达式)没有任何乘数 - 也许它应该跟一个*,a +或a?另外,我认为|符号意味着在您的第一个字符类和第or前面的第二个字符类之间执行% - 正如它现在看起来那样,它是在它之间进行的第一个字符类和%符号。它可能应该像|(%[0-9A-F]{2}))*$

答案 2 :(得分:-1)

您应该使用rfc regexp来检查每个可能的字符。看this