正则表达式解析行并捕获字符串和逗号分隔的数字

时间:2018-11-09 21:20:57

标签: php regex string parsing delimited-text

我正在尝试使用类似于以下内容的行来解析文件:

       John David James (DEM) .  .  .  .  .  .     7,808   10.51
       Marvin D. Scott (DEM)  .  .  .  .  .  .     6,548    9.55
       Maria "Mary" Williams (DEM)  .  .  .  .     4,551    8.58
       Dwayne R. Johnson.  .  .  .  .  .  .  .     4,322    8.22
       WRITE-IN.  .  .  .  .  .  .  .  .  .  .       188     .29

我需要在第一列中记录名称和编号。最终结果将是

John David James (DEM),7808
Marvin D. Scott (DEM),6548
Maria "Mary" Williams (DEM),4551
Dwayne R. Johnson,4322
WRITE-IN,188

我尝试过

\s*\b(.*)\b(\s*\.\s*.*)(\d+,\d+|\d+)\b
\s*\b(.*)\b(\.|.\s)+\b(\d+,\d+|\d+)\b

有什么建议吗?

3 个答案:

答案 0 :(得分:1)

如果数据是按列对齐的(所有列都具有已知的固定宽度),则使用诸如substr之类的字符串函数:

<?php
$lines = '
       John David James (DEM) .  .  .  .  .  .     7,808   10.51
       Marvin D. Scott (DEM)  .  .  .  .  .  .     6,548    9.55
       Maria "Mary" Williams (DEM)  .  .  .  .     4,551    8.58
       Dwayne R. Johnson.  .  .  .  .  .  .  .     4,322    8.22
       WRITE-IN.  .  .  .  .  .  .  .  .  .  .       188     .29
';

foreach(preg_split('/(\\r|\\n)+/', $lines) as $line) {
    if ($line === '') continue;
    $name = substr($line, 0, 46);
    $amount = substr($line, 46, 10);
    $name = rtrim(ltrim($name), " .");
    $amount = (float) str_replace(",", "", $amount);
    echo $name . ", " . $amount;
}

答案 1 :(得分:1)

您可以使用UNGREEDY正则表达式来实现它。

在这里,当我们捕获名称时,我们希望“任何字符序列后跟点和空格序列”。因此,这里是等效的正则表达式:(.+)[. ]*

但是引擎默认设置为贪婪模式。会发生什么?第一部分(.+)不会停在遇到的第一个点或第一个空格处。为什么?因为可以在行尾执行整个正则表达式,并且引擎将采用此路径,就像在贪婪模式下一样。

您可以在下面的工作代码中看到整个正则表达式。第一个捕获组将捕获名称字段之外的对象。

我们需要告诉他“吃掉”较不匹配的部分。

<?php
$lines = '
       John David James (DEM) .  .  .  .  .  .     7,808   10.51
       Marvin D. Scott (DEM)  .  .  .  .  .  .     6,548    9.55
       Maria "Mary" Williams (DEM)  .  .  .  .     4,551    8.58
       Dwayne R. Johnson.  .  .  .  .  .  .  .     4,322    8.22
       WRITE-IN.  .  .  .  .  .  .  .  .  .  .       188     .29
';
$lines = explode("\n", $lines);

// Here, the U flag sets the ungreedy mode
$pattern = '/^\s*(\S.+\S)[. ]+([0-9]+)(?:,([0-9]+))?\s.*$/U';
echo "<pre>";
foreach ($lines  as $line) {
    // Here : - ${1} will capture the name,
    //        - ${2} the integer part of the number
    //        - ${3} the decimal part
    echo preg_replace($pattern, '${1},${2}${3}', $line) . "\n";
}
echo "</pre>";
?>

结果:

John David James (DEM),7808
Marvin D. Scott (DEM),6548
Maria "Mary" Williams (DEM),4551
Dwayne R. Johnson,4322
WRITE-IN,188

答案 2 :(得分:1)

此模式通过在名称后找到点序列来捕获名称。
然后捕获一个数字和逗号模式作为数字。

然后,我循环构建新数组,并用逗号替换逗号。

$str = '       John David James (DEM) .  .  .  .  .  .     7,808   10.51
       Marvin D. Scott (DEM)  .  .  .  .  .  .     6,548    9.55
       Maria "Mary" Williams (DEM)  .  .  .  .     4,551    8.58
       Dwayne R. Johnson.  .  .  .  .  .  .  .     4,322    8.22
       WRITE-IN.  .  .  .  .  .  .  .  .  .  .       188     .29';
preg_match_all("/\s*(.*?)\s*\.  \..*?([\d,]+)/", $str, $matches);

foreach($matches[1] as $key => $name){
    $new[] = $name . "," . str_replace(",", "", $matches[2][$key]);
}


var_dump($new);

输出:

array(5) {
  [0]=>
  string(27) "John David James (DEM),7808"
  [1]=>
  string(26) "Marvin D. Scott (DEM),6548"
  [2]=>
  string(32) "Maria "Mary" Williams (DEM),4551"
  [3]=>
  string(22) "Dwayne R. Johnson,4322"
  [4]=>
  string(12) "WRITE-IN,188"
}

https://3v4l.org/SdqoZ