【问题标题】:PhP regex for a string, what would be the best way to do it?字符串的 PhP 正则表达式,最好的方法是什么?
【发布时间】:2017-07-07 08:43:45
【问题描述】:

我有一个带有rule 字段的数组,其中包含这样的字符串:

FREQ=MONTHLY;BYDAY=3FR
FREQ=MONTHLY;BYDAY=3SA
FREQ=WEEKLY;UNTIL=20170728T080000Z;BYDAY=MO,TU,WE,TH,FR
FREQ=MONTHLY;UNTIL=20170527T100000Z;BYDAY=4SA
FREQ=WEEKLY;BYDAY=SA
FREQ=WEEKLY;INTERVAL=2;BYDAY=TH
FREQ=WEEKLY;BYDAY=TH
FREQ=WEEKLY;UNTIL=20170610T085959Z;BYDAY=SA
FREQ=MONTHLY;BYDAY=2TH

每一行都是一个不同的数组,我提供了一些线索来了解我需要什么。

我需要的是写一个正则表达式来去掉所有不必要的值。

所以,我不需要 FREQ= ; BYDAY= 等。我基本上需要 = 之后的值,但我想将每个值存储在不同的变量中。

以第三个为例:

$frequency = WEEKLY
$until = 20170728T080000Z
$day = MO, TU, WE, TH, FR

它不一定是一个正则表达式,每个值可以有一个正则表达式。所以我有一个 FREQ:

preg_match("/[^FREQ=][A-Z]+/", $input_line, $output_array);

但很遗憾,我无法做到这一点,我该如何解决这个问题?

【问题讨论】:

  • 不,不是爆炸()。 preg_split() 是解决方案!
  • 如果应该使用 preg_split,你能提供一个解决方案吗?据我所知 preg_split 会给出一个结果数组并记住每个值都必须转到一个变量

标签: php regex laravel laravel-5


【解决方案1】:

唯一的方法是 PHP 数组解构:

$str = "FREQ=WEEKLY;UNTIL=20170728T080000Z;BYDAY=MO,TU,WE,TH,FR";

preg_match_all('~(\w+)=([^;]+)~', $str, $matches);
[$freq, $until, $byday] = $matches[2]; // As of PHP 7.1 (otherwise use list() function)
echo $freq, " ", $until, " ", $byday;
// WEEKLY 20170728T080000Z MO,TU,WE,TH,FR

Live demo

更笼统

使用extract函数:

preg_match_all('~(\w+)=([^;]+)~', $str, $m);
$m[1] = array_map('strtolower', $m[1]);
$vars = array_combine($m[1], $m[2]);
extract($vars);
echo $freq, " ", $until, " ", $byday;

Live demo

【讨论】:

  • 我喜欢这个,但是根据 OPs 示例,数据的格式不是很好——所以这并不能解决手头的问题。
  • 我在比赛前得到了意想不到的'='
  • ...数据格式不正确我不明白,@TomRegner。
  • 好吧,我有 php 7.1,所以这应该不是问题,对于@TomRegner 评论,它是正确的,你指定一个字符串。我有 976 个数组
  • @revo 并非每一行都包含相同类型的数据,有时有 UNTIL,有时没有,等等。还有 AFAIU 其他 cmets,位置也可能不同
【解决方案2】:

注意:对于这个问题,我推荐@revo 发布的generell 方法,它简洁、安全且易于使用——但请记住,与固定字符串函数相比,正则表达式会带来性能损失,所以如果您可以使用 strpos/substr/explode/...,尝试使用它们,不要“下意识地”使用基于 preg_ 的解决方案。

由于分隔符是固定的并且似乎不会出现在您感兴趣的值中,并且您还依赖于键(FREQ: 等)的知识,因此您不需要正则表达式(尽可能我喜欢在任何可以使用的地方使用它们,你可以在这里使用它们);在这种情况下为什么不简单地爆炸和分裂?

$lines = explode("\n", $text);
foreach($lines as $line) {
    $parts = explode(';', $line);
    $frequency = $until = $day = $interval = null;
    foreach($parts as $part) {
        list($key, $value) = explode('=', $part);
        switch($key) {
             case 'FREQ':
                   $frequency = $value;
                   break;
             case 'INTERVAL':
                   $interval = $value;
                   break;
              // and so on
         }
    }
    doSomethingWithTheValues();
}

如果您的用例像声明的那样简单,这可能会更易读和更有效。

【讨论】:

  • 这似乎是最好的解决方案,它有效,我假设如果变量为空,它将忽略它并继续
【解决方案3】:

你需要使用模式

;?[A-Z]+=

连同 preg_split();

preg_split('/;?[A-Z]+=/', $str);

解释

;       match Semikolon
?       no or one of the last Character
[A-Z]+  match one or more uppercase Letters
=       match one =

如果你想把每一行放到一个单独的数组中,你应该这样做:

 # split each Line into an Array-Element
 $lines = preg_split('/[\n\r]+/', $str);

 # initiate Array for Results
 $results = array();

 # start Looping trough Lines
 foreach($lines as $line){

  # split each Line by the Regex mentioned above and
  # put the resulting Array into the Results-Array
  $results[] = preg_split('/;?[A-Z]+=/', $line);

 }

【讨论】:

  • 这一切都很好,但它给了我一个值数组,我如何通过它并将它保存到不同的变量?随着每个值进入数据库中的不同字段
  • 我更新了答案。这就是你可以用 preg_split() 做的所有事情。
  • 这给了我一个数组内的数组,但不幸的是我需要变量保存到数据库,因为我不能去 $data = ['frequency' => $results[1]] 因为频率有时不同数组编号
猜你喜欢
  • 1970-01-01
  • 2016-10-12
  • 1970-01-01
  • 2015-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-26
相关资源
最近更新 更多