我有一个PHP正则表达式,如何添加一个条件的字符数


I have a PHP regEx, how do add a condition for the number of characters?

我有一个正则表达式,我在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语句来清除它们。

分割的魔力。我最初的假设在技术上是不正确的(尽管这是一个更容易得到的解决方案)。让我们检查一下分割模式:

('/|'.|-|_|=|'?|'&|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ä
)

这些例程按要求工作。但这的确是要付出性能代价的。成本与旧答案相似。

相关问题:

  • 与Regex反匹配
  • 按分隔符拆分字符串,但如果已转义则不拆分

旧答案,执行两步处理(首先分割,然后过滤)

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

所以你能做的就是过滤结果。您可以使用正则表达式(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
)

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

检测哪些参数是有用的,哪些是无用的不应该是这段代码的一部分。一个简短的参数是否相关,每页可能会有所不同。

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

我认为,如果你试图从URL中获得意义,你实际上会想要以一种不需要复杂正则表达式来获得值的方式编写干净的URL。

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

所以你创建的是干净的URL,比如

/value1/value2/value3

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

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

试试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

希望能有所帮助

不要使用正则表达式来分隔路径。请使用explode

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

然后,如果您需要拆分数组中的单个元素,那么就这样做,就像在末尾的"pagerank-update"元素一样。

编辑:

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

三个离散步骤:

  • $dirs = explosion…
  • 删除参数<3字符
  • 在文件末尾分解文件参数

如果你把你的逻辑分解成离散的逻辑块,而不是试图让regex做所有的事情,那就清楚得多了。