PHP正则表达式匹配UNC路径

3
我需要一些在PHP中使用的正则表达式来验证通过表单传递的UNC路径。 它应该是以下格式:
\\server\something

“...并允许进行更深层次的子文件夹。为了保持一致性,去掉末尾的斜杠可能是有必要的,尽管如果需要的话我可以很容易地通过substr函数来完成这个操作。”
“我在网上读到,在PHP中匹配一个反斜杠需要4个反斜杠(使用类C的字符串时),我认为我知道为什么会这样(PHP转义(例如2 = 1,所以4 = 2),然后正则表达式引擎转义(剩余的2 = 1)。我见过以下两种被引用为等效合适的正则表达式来匹配单个反斜杠:”
$regex = "/\\\\/s";

或者显然也是这样的:
$regex = "/[\\]/s";

然而,这些会产生不同的结果,而且这与我的最终目的略有不同,即匹配完整的UNC路径。
为了查看是否能匹配两个反斜杠,我使用以下内容进行测试:
$path = "\\\\server";
echo "the path is: $path <br />"; // which is \\server
$regex = "/\\\\\\\\\/s";
if (preg_match($regex, $path)) 
{
    echo "matched";
}
else
{
    echo "not matched";
}

上述内容似乎匹配了两个或更多的反斜杠:(模式为8个斜杠,相当于2个,那么为什么输入3个反斜杠($path = "\\\\\\server")也会匹配呢?)
我认为可能以下内容可以解决这个问题:
$regex = "/[\\][\\]/s";

再一次,没有 :(
请帮帮我,不然我要跳窗口了,哈哈 :)
2个回答

6
请使用这个小宝石:
$UNC_regex = '=^\\\\\\\\[a-zA-Z0-9-]+(\\\\[a-zA-Z0-9`~!@#$%^&(){}\'._-]+([ ]+[a-zA-Z0-9`~!@#$%^&(){}\'._-]+)*)+$=s';

来源:http://regexlib.com/REDetails.aspx?regexp_id=2285 (已采用PHP字符串转义)

上述RegEx匹配有效主机名(仅允许少量有效字符)和主机名后的路径部分(允许许多但不是所有字符)。


关于反斜杠问题的附注:

  • 当使用双引号(")包含字符串时,必须注意PHP特殊字符转义..."\\"在 PHP 中是一个单独的\

  • 重要提示:即使使用单引号('),这些反斜杠也必须被转义。
    带有单引号的 PHP 字符串将按字面意义(未转义)处理字符串中的所有内容,但有一些例外情况:
    1. 反斜杠后跟反斜杠 (\\) 将被解释为单个反斜杠。 ('C:\\*.*' => C:\*.*)
    2. 反斜杠后跟单引号 (\') 将被解释为单引号。 ('I\'ll be back' => I'll be back)
    3. 反斜杠后跟任何其他字符将被解释为反斜杠。 ('Just a \ somewhere' => Just a \ somewhere)

  • 此外,必须了解PCRE转义序列
    RegEx解析器将 \ 视为字符类,因此您需要再次针对RegEx进行转义。
    要匹配两个 \\,您必须编写 $regex = "\\\\\\\\"$regex = '\\\\\\\\'

    来自PHP关于PCRE转义序列的文档:

单引号和双引号的 PHP 字符串具有反斜杠的特殊含义。因此,如果要使用正则表达式\匹配\,则在 PHP 代码中必须使用 "\\"或'\\\\'。


关于您的问题:

为什么三个反斜杠的输入 ($path="\ \ \server") 能够匹配正则表达式 "/\\\\\\\\/s"

原因是您没有定义边界(使用^表示字符串的开头,$表示字符串的结尾),因此它会在"某个地方"发现\\并得到一个正匹配结果。要获得期望的结果,您应该像这样做:
$regex = '/^\\\\\\\\[^\\\\]/s';

上述正则表达式有两个修改:

  • 在开头加上^,只匹配字符串开头的两个\\
  • 使用负字符类[^\\]表示:后面不跟随另一个反斜杠

关于你最后的正则表达式:

$regex = "/[\\][\\]/s";

你在这里对反斜杠转义存在困惑(请参见上文以获得澄清)。"/[\\][\\]/s"在PHP中被解释为/[\][\]/s,这将导致正则表达式失败,因为\是正则表达式中的保留字符,因此必须进行转义。
你的这个正则表达式变体可以工作,但也会因为我已经解释过的原因而匹配任何两个反斜杠的出现。
$regex = '/[\\\\][\\\\]/s';

谢谢您的解释 :) 我尝试了regexlib中的正则表达式,但它破坏了某些东西。在Notepad++中,被''包围的正则表达式通常是完全灰色的。这个是灰色的,直到:$UNC_regex = '=^\\\\[a-zA-Z0-9-]+\\[a-zA-Z0-9`~!@#$%^&(){}'此后的字符为彩色: .-]+([ ]+[a-zA-Z0-9`~!@#$%^&(){}'.-]+)*$=s';另外,考虑到您所说的,为什么所有这些都报告未匹配?我本以为这将匹配第二个if / else:http://pastebin.com/BSnJrnFQ - Robin
哈哈,谢谢Kaii!现在我的理解和经验/结果是一致的。我想问一下,$UNC_regex实际上必须修改为:$regex ='= ^\\\\[a-zA-Z0-9-]+\\ [a-zA-Z0-9~!@#$%^&(){}\'._-]+([ ]+[a-zA-Z0-9~!@#$%^&(){}'._-]+)*$ =s';才能正常工作吗?(添加了额外的反斜杠)。当我这样做时,它似乎可以工作。 - Robin
看来stackoverflow在粘贴这个正则表达式时会去掉一个反斜杠。但是我的实际代码中有8和4个反斜杠! - Robin
@Rob "no match" 是预期的结果 ;) 你需要一个主机名 共享名称才能得到有效的 UNC 路径。例如 \\foo\bar - Kaii
这个答案是错误的,而且在谷歌搜索结果中排名第一。根据 https://msdn.microsoft.com/en-us/library/gg465305.aspx ,UNC路径允许包含IPv6地址或IPv4地址,并且RFC3986中的reg-name部分可以包含“-”、“.”、“_”和“~”。 - Jos van Egmond
显示剩余3条评论

3

同时也要输出正则表达式,这样你就能看到实际的模式,PHP中写这些斜杠可以变得很麻烦,所以你可以验证它是否正确。

另外,你应该在模式开头加上^以匹配字符串开头,并在末尾加上$来指定整个字符串必须匹配。

\\server\something

正则表达式:

 ~^\\\\server\\something$~

PHP字符串:

$pattern = '~^\\\\\\\\server\\\\something$~';

针对重复的部分,您希望表达一个服务器存在,并且其后跟随一个或多个 \something 部分。如果 server 就像 something,那么可以简化为:

^\\(?:\\[a-z]+){2,}$

PHP字符串:

$pattern = '~^\\\\(?:\\\\[a-z]+){2,}$~';

由于在单引号字符串中如何书写\字符存在一些混淆:

# Output:
#
# * Definition as '\\' ....... results in string(1) "\"
# * Definition as '\\\\' ..... results in string(2) "\\"
# * Definition as '\\\\\\' ... results in string(3) "\\\"

$slashes = array(
    '\\',
    '\\\\',
    '\\\\\\',
);

foreach($slashes as $i => $slashed) {
    $definition = sprintf('%s ', var_export($slashed, 1));
    ob_start();
    var_dump($slashed);
    $result = rtrim(ob_get_clean());    
    printf(" * Definition as %'.-12s results in %s\n", $definition, $result);
}

@Kaii:不,我不这么认为。你可以证明我错,但是在单引号字符串中进行反斜杠转义应该像示例中所示一样有效,请参见字符串:单引号 - hakre
是的,我刚刚意识到这一点并重新修改了我的答案。我正准备撤销我的编辑,但你已经做了 :) - Kaii
@Kaii: 我添加了一些代码块,只是写模式时大量斜杠可能会变得很尴尬... ;) - hakre
谢谢hakre,也感谢你帮助Kaii帮助我。使用http://pastebin.com/KSByxEwC和您的正则表达式`$regex = '^\\(?:\\[a-z]+){2,}$';`,所有3个条件都无法匹配。 - Robin

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