【问题标题】:Regex to match all lines except duplicate正则表达式匹配除重复的所有行
【发布时间】:2019-08-12 18:58:23
【问题描述】:

我有这段文字:

156.48.459.20 - - [11/Aug/2019
156.48.459.20 - - [11/Aug/2019
235.145.41.12 - - [11/Aug/2019
235.145.41.12 - - [11/Aug/2019
66.23.114.251 - - [11/Aug/2019

我想匹配当天的所有行,所以我做了这个简单的正则表达式'/.*11\/Aug\/2019.*'

你可以看到文本中有两个重复的 IP,我不想匹配重复的行,所以我搜索了一下,我发现了这个正则表达式:(.).*\1DEMO 虽然这个正则表达式有点奇怪我试图在我当前的正则表达式中应用它,所以我做了:(.*11\/Aug\/2019.*)\1,它没有用。有人可以帮忙吗?

这是我想要的结果:

156.48.459.20 - - [11/Aug/2019
235.145.41.12 - - [11/Aug/2019
66.23.114.251 - - [11/Aug/2019

注意:我正在使用函数preg_match_all()

preg_match_all('/(.*11\/Aug\/2019.*)\1/', $input_lines, $output_array);

【问题讨论】:

标签: php regex pcre preg-match-all


【解决方案1】:

纯正则表达式是必需的吗?

您可以使用 PHP 来获取唯一性:

<?php
$input_lines = '156.48.459.20 - - [11/Aug/2019
156.48.459.20 - - [11/Aug/2019
235.145.41.12 - - [11/Aug/2019
235.145.41.12 - - [11/Aug/2019
66.23.114.251 - - [11/Aug/2019';

preg_match_all( '/.*11\/Aug\/2019/m', $input_lines, $output_array );

// PHP associative array abuse incoming
// Flip the array so that the values become keys and flip it back
// This guarantees that only uniques survive
$output_array[ 0 ] = array_keys( array_flip( $output_array[ 0 ] ) );

var_dump( $output_array );

输出:

array(1) {
  [0]=>
  array(3) {
    [1]=>
    string(30) "156.48.459.20 - - [11/Aug/2019"
    [3]=>
    string(30) "235.145.41.12 - - [11/Aug/2019"
    [4]=>
    string(30) "66.23.114.251 - - [11/Aug/2019"
  }
}

【讨论】:

  • 这当然很有创意....但它没有解释它的作用,而 array_unique() 可以。
  • @AbraCadaver 我记得基准测试证明这更快...
  • 没错。 Array_unique 实际上是获取唯一项最慢的方法。如果我没记错的话,Array_column(arr, null, key) 是最快的,但这使它具有关联性。 Array_column 将数组迭代一次,array_flip & array_flip 做两次,自然...
  • @Andreas 我可能不得不稍微把脚从嘴里拉出来。 PHP array_flip() 显示更好的性能,但 PHP >= 7.2 有利于 array_unique()。我认为主要的收获是,过早的优化是徒劳的,除非我们在谈论 ++$i$i++
  • @KIKOSoftware 文档不是太远,但我会添加一些 cmets...
【解决方案2】:

它几乎是一个 1 班轮

'~(?m)^(?:([\d.]*[- ]*\[11/Aug/2019.*)\R*(?=[\S\s]*?\1)|(?!.*\[11/Aug/2019).*\R*)~'

Sample

php

 $target = <<<'EOS'
 156.48.459.20 - - [11/Aug/2019
 156.48.459.20 - - [11/Aug/2019
 235.145.41.12 - - [11/Aug/2019
 235.145.41.12 - - [11/Aug/2019
 66.23.114.251 - - [11/Aug/2019
 66.23.114.251 - - [09/Aug/2019
 156.48.459.20 - - [11/Aug/2019
 235.145.41.12 - - [11/Aug/2019
 66.23.114.251 - - [01/Aug/2019
 66.23.114.251 - - [11/Aug/2019
 235.145.41.12 - - [11/Aug/2019
 EOS;


 $res = preg_replace ( '~(?m)^(?:([\d.]*[- ]*\[11/Aug/2019.*)\R*(?=[\S\s]*?\1)|(?!.*\[11/Aug/2019).*\R*)~', '', $target );

 echo $res."\n";

输出

156.48.459.20 - - [11/Aug/2019
66.23.114.251 - - [11/Aug/2019
235.145.41.12 - - [11/Aug/2019

更好地了解它

 (?m)
 ^ 
 (?:
      ( [\d.]* [- ]* \[ 11/Aug/2019 .* )  # (1)
      \R* 
      (?= [\S\s]*? \1 )
   |  
      (?! .* \[ 11/Aug/2019 )
      .*  \R* 
 )

【讨论】:

    【解决方案3】:
    $txt = <<<'EOD'
    156.48.459.20 - - [11/Aug/2019
    156.48.459.20 - - [11/Aug/2019
    235.145.41.12 - - [11/Aug/2019
    235.145.41.12 - - [11/Aug/2019
    66.23.114.251 - - [11/Aug/2019
    EOD;
    
    $url = 'data:text/plain;base64,' . base64_encode($txt);
    // change this line with the url of your log file: $url = '/path/to/file.log';
    
    $result = [];
    
    if ( false !== $handle = fopen($url, 'r') ) {
        while ( false !== $data = fgetcsv($handle, 1000, ' ') ) {
            if ( $data[3] === '[11/Aug/2019' )
                $result[$data[0]] = 1;
        }
    }
    
    $result = array_keys($result);
    
    print_r($result);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-12-26
      • 2013-05-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多