1

我有一个正则表达式,我在 php 中使用:

$word_array = preg_split(
    '/(\/|\.|-|_|=|\?|\&|html|shtml|www|php|cgi|htm|aspx|asp|index|com|net|org|%|\+)/',
    urldecode($path), NULL, PREG_SPLIT_NO_EMPTY
);

它工作得很好。它需要一大块 url 参数,例如:

/2009/06/pagerank-update.html

并返回一个数组,如:

array(4) {
  [0]=>
  string(4) "2009"
  [1]=>
  string(2) "06"
  [2]=>
  string(8) "pagerank"
  [3]=>
  string(6) "update"
}

我唯一需要的是它也不返回少于 3 个字符的字符串。所以"06"字符串是垃圾,我目前正在使用 if 语句来清除它们。

4

5 回答 5

4

分裂的魔力。我最初的假设在技术上是不正确的(尽管更容易找到一个解决方案)。因此,让我们检查您的拆分模式:

(\/|\.|-|_|=|\?|\&|html|shtml|www|php|cgi|htm|aspx|asp|index|com|net|org|%|\+)

我稍微重新安排了一下。不需要外括号,我将单个字符移到最后的字符类中:

 html|shtml|www|php|cgi|htm|aspx|asp|index|com|net|org|[\/._=?&%+-]

这对于一些预先排序。让我们将这种模式称为拆分模式,s简而言之并对其进行定义。

您希望匹配不属于 split-at 模式中的那些字符的所有部分,并且至少匹配三个字符。

我可以通过以下模式实现这一点,包括支持正确的拆分序列和 unicode 支持。

$pattern    = '/
    (?(DEFINE)
        (?<s> # define subpattern which is the split pattern
            html|shtml|www|php|cgi|htm|aspx|asp|index|com|net|org|
            [\\/._=?&%+-] # a little bit optimized with a character class
        )
    )
    (?:(?&s))          # consume the subpattern (URL starts with \/)
    \K                 # capture starts here
    (?:(?!(?&s)).){3,} # ensure this is not the skip pattern, take 3 characters minimum
/ux';

或者更小:

$path       = '/2009/06/pagerank-update.htmltesthtmltest%C3%A4shtml';
$subject    = urldecode($path);
$pattern    = '/(?(DEFINE)(?<s>html|shtml|www|php|cgi|htm|aspx|asp|index|com|net|org|[\\/._=?&%+-]))(?:(?&s))\K(?:(?!(?&s)).){3,}/u';
$word_array = preg_match_all($pattern, $subject, $m) ? $m[0] : [];
print_r($word_array);

结果:

Array
(
    [0] => 2009
    [1] => pagerank
    [2] => update
    [3] => test
    [4] => testä
)

也可以使用相同的原理preg_split。有点不同:

$pattern = '/
    (?(DEFINE)       # define subpattern which is the split pattern
        (?<s>
    html|shtml|www|php|cgi|htm|aspx|asp|index|com|net|org|
    [\/._=?&%+-]
        )
    )
    (?:(?!(?&s)).){3,}(*SKIP)(*FAIL)       # three or more is okay
    |(?:(?!(?&s)).){1,2}(*SKIP)(*ACCEPT)   # two or one is none
    |(?&s)                                 # split @ split, at least
/ux';

用法:

$word_array = preg_split($pattern, $subject, 0, PREG_SPLIT_NO_EMPTY);

结果:

Array
(
    [0] => 2009
    [1] => pagerank
    [2] => update
    [3] => test
    [4] => testä
)

这些例程按要求工作。但这确实有其价格和性能。成本与旧答案相似。

相关问题:


旧答案,进行两步处理(先拆分,然后过滤)

因为您使用的是拆分例程,所以无论长度如何,它都会拆分。

所以你可以做的是过滤结果。您可以使用正则表达式 ( preg_filter) 再次执行此操作,例如删除所有较小的三个字符的表达式:

$word_array = preg_filter(
    '/^.{3,}$/', '$0', 
    preg_split(
        '/(\/|\.|-|_|=|\?|\&|html|shtml|www|php|cgi|htm|aspx|asp|index|com|net|org|%|\+)/',
        urldecode($path), 
        NULL, 
        PREG_SPLIT_NO_EMPTY
    )
);

结果:

Array
(
    [0] => 2009
    [2] => pagerank
    [3] => update
)
于 2012-12-21T17:36:20.667 回答
1

我猜你正在构建某种 URL 路由器。

检测哪些参数有用,哪些参数不应该是此代码的一部分。短参数是否相关可能因页面而异。

在这种情况下,你不能忽略第一个元素吗?您的页面应该(或“处理程序”)应该知道要使用哪些参数来调用它,它应该进行分类。

于 2012-12-21T17:31:50.403 回答
1

我认为,如果您试图从 URL 中获取含义,那么您实际上希望以不需要复杂的正则表达式来获取值的方式编写干净的 URL。

在许多情况下,这涉及使用服务器重定向规则和前端控制器或请求路由器。

所以你构建的是干净的 URL

/value1/value2/value3

URL 中根本没有任何.html,.php等。

在我看来,您没有充分解决进入系统(即 Web 服务器)的问题,以使您的 URL 解析尽可能简单。

于 2012-12-21T17:38:25.197 回答
1

尝试preg_match()而不是怎么样preg_split()

模式(使用断言):

/([a-z0-9]{3,})(?<!htm|html|shtml|www|php|cgi|htm|aspx|asp|index|com|net|org)/iu

函数调用:

$pattern = '/([a-z0-9]{3,})(?<!htm|html|shtml|www|php|cgi|htm|aspx|asp|index|com|net|org)/iu';
$subject = '/2009/06/pagerank-update.html';
preg_match_all($pattern, $subject, $matches);
print_r($matches);

您可以在此处尝试该功能:functions-online.com/preg_match_all.html

希望这可以帮助

于 2012-12-21T17:56:37.690 回答
-3

不要使用正则表达式来分解该路径。只需使用explode.

$dirs = explode( '/', urldecode($path) );

然后,如果您需要拆分数组的单个元素,请执行此操作,就像最后的“pagerank-update”元素一样。

编辑:

关键是你有两个不同的问题。首先,您要拆分斜杠上的路径元素。然后,您想将文件名分解成更小的部分。不要试图把所有东西都塞进一个试图做所有事情的正则表达式中。

三个离散步骤:

  • $dirs = 爆炸...
  • 清除参数 < 3 字符
  • 最后分解文件参数

如果您将逻辑分解为离散的逻辑块而不是试图让正则表达式完成所有事情,那就更清楚了。

于 2012-12-21T17:28:39.093 回答