【问题标题】:Regex to parse line with and capture string and comma separated number正则表达式解析行并捕获字符串和逗号分隔的数字
【发布时间】:2019-04-13 11:27:25
【问题描述】:

我正在尝试解析一个文件,其行类似于:

       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

有什么建议吗?

【问题讨论】:

  • 数据是否总是列对齐?
  • @SalmanA 是的。他们使用句点和空格来分隔名称和数字
  • 然后使用 substr.不是正则表达式。
  • @SalmanA 名称的长度不同,值可以是 1 - 5 位。

标签: php regex string parsing csv


【解决方案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;
}

【讨论】:

    【解决方案2】:

    您可以使用 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
    

    【讨论】:

    • 拆分()?来自手册:此函数在 PHP 5.3.0 中已弃用,在 PHP 7.0.0 中已删除。。为了清楚起见,我没有投反对票。我只是写了这个作为为什么使用不推荐使用的函数。
    • 是的,我看到了您的评论并修复了我的代码。我正忙于添加更多解释。谢谢。
    • 再提醒一下,OP 不想要数字中的逗号。
    • 感谢非常详细的描述!
    • 谢谢阿梅西赫尔。您的回复很棒,但我选择了@Andreas 版本,因为他提供的代码给了我名称并算作我可以单独使用的变量。我将名称和数字转换为 json 数组以在其他地方使用。
    【解决方案3】:

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

    然后我循环构建新数组并将逗号替换为空。

    $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

    【讨论】:

    • 谢谢@Andreas。这很好用。这个版本实际上更简化了我的工作,因为我可以分别使用名称和计数。
    猜你喜欢
    • 2016-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多