【问题标题】:Regex to allow one or more words allowing one space and using two spaces or more as a column正则表达式允许一个或多个单词允许一个空格并使用两个或多个空格作为一列
【发布时间】:2017-05-01 20:49:49
【问题描述】:

我正在尝试逐行解析一些文件并尝试将其识别为列。连续的两列是单词,但分隔模式不止一个空格。由于列之间可以有空格,因此我在将这两者分开时遇到了一些麻烦。

行示例:

2236        ARGEMIRO PATROCINIO                                   ARGEMIRO                 I       I          UBC            3,8462

1150721     ZACHARY F CONDON                                      ZACH CONDON               I       I          FINTAGE        8,3333

50300       COMERCIAL FONOGRAFICA RGE LTDA.                                                 PF      LI         ABRAMUS       25,0000`

(固定)

obs.:它没有显示 '2236'、'ARGEMIRO 之间的所有空格 PATROCINIO'、'ARGEMIRO'、'I'、'I'、'UBC' 和 '3,8462'

我正在使用这个正则表达式:

(\d+)\s+([\.a-zA-Z\s,'À-úÀ-ÿ()\?\-\/\d]+)\s{2,}([\.a-zA-Z\s,'À-úÀ-ÿ()\?\-\/\d]+)\s{2,}(I|PF|MA)\s{2,}(I|PF|PL|LI|MA|CV|MJ)\s{2,}(\w+)\s{2,}(\d+,\d{4})

但不幸的是,“ARGEMIRO PATROCINIO”将与第二个“ARGEMIRO”一起出现; "ZACHARY F CONDON" 和第二个 "ZACH CONDON" 等等。

所以,

  1. 如何修复这个正则表达式来分隔这两个“列”?
  2. 另一个正则表达式如何能够在这 7 列中的两个或多个空格之间抓取任何内容?

谢谢!

【问题讨论】:

  • preg_split('/\s+/',.. ?
  • 我认为preg_split 看起来比this "fix" 更整洁。
  • @WiktorStribiżew 为什么要放这两个“?”正则表达式开始工作? preg_split 做得很好,但是这个正则表达式维护了列结构,所以我可以检测我正在读取什么类型的数据。你可以解释吗?也许作为答案?
  • 我想你也在使用/U修饰符,对吧?它逆转了贪婪。因此,当您将*?+?/U 一起使用时,它们实际上是贪婪

标签: php regex parsing


【解决方案1】:

我实际上并没有在您粘贴的数据中看到双空格,但您是这样描述的。您可以这样做以拆分任何有 2 个或更多连续空格的位置:

preg_split("/[\s]{2,}/", $data);

DEMO:http://www.phpliveregex.com/p/jWZ(点击右侧“preg_split”)

【讨论】:

  • 是的,当我将它放入代码标记中时,它只会出现一个空格。 preg_split 似乎可以完成这项工作,但它不会保留没有数据的列,例如:163587 WELLINGTON POMPEU DO NASCIMENTO MA MA 1,1857
【解决方案2】:

您应该了解贪婪是如何运作的。一旦你的子模式变得懒惰,它首先被跳过,并且首先尝试后续模式。只有在没有找到匹配项的情况下,引擎才会返回到延迟量化的模式,匹配该模式匹配的单个字符并再次继续测试后续子模式。该机制类似于回溯,但向前推进。

所以,您可以做的是确保第二列和第三列模式是惰性的。 (注意我猜你正在使用/U贪婪交换修饰符,我的建议是不要使用它来使模式尽可能清晰):

(\d+)\s+([-.a-zA-Z\s,'À-úÀ-ÿ()?\/\d]+?)\s{2,}([-.a-zA-Z\s,'À-úÀ-ÿ()?\/\d]+?)\s{2,}(I|PF|MA)\s{2,}(I|PF|PL|LI|MA|CV|MJ)\s{2,}(\w+)\s{2,}(\d+,\d{4})

添加锚点(^ 在开头,$ 在结尾)和 /m 修饰符,如果您只需要匹配整行。

请参阅regex demo

查看[-.a-zA-Z\s,'À-úÀ-ÿ()?\/\d]+?) 模式,+? lazy quantifier 匹配 1+ 个字符,尽可能少

注意我也做了一些修饰:. 不需要在字符类中转义,-,当放置在字符类的开头时,不需要转义来表示文字-.

【讨论】:

    【解决方案3】:

    我会说通常需要这个正则表达式

    /(\d+)\s{2,}([.a-zA-Z,'À-úÀ-ÿ()?\-\/\d]+(?:\s?[.a-zA-Z,'À-úÀ-ÿ()?\-\/\d])*)\s{2,}([.a-zA-Z,'À-úÀ-ÿ()?\-\/\d]+(?:\s?[.a-zA-Z,'À-úÀ-ÿ()?\-\/\d])*)\s{2,}(I|PF|MA)\s{2,}(I|PF|PL|LI|MA|CV|MJ)\s{2,}(\w+)\s{2,}(\d+,\d{4})/

    但由于最后一条记录只有 6 列,因此与最后一条记录不匹配 https://regex101.com/r/YynbpP/1

    我的建议是您重新考虑哪些列可以是可选的。
    然后相应地调整正则表达式。

    例如,第 2 组和第 3 组的结构相同。
    如果您希望第二个是可选的,那么正确的正则表达式是:

    /(\d+)\s{2,}([.a-zA-Z,'À-úÀ-ÿ()?\-\/\d]+(?:\s?[.a-zA-Z,'À-úÀ-ÿ()?\-\/\d])*)(?|\s{2,}((?:[.a-zA-Z,'À-úÀ-ÿ()?\-\/\d]+(?:\s?[.a-zA-Z,'À-úÀ-ÿ()?\-\/\d])*))|())\s{2,}(I|PF|MA)\s{2,}(I|PF|PL|LI|MA|CV|MJ)\s{2,}(\w+)\s{2,}(\d+,\d{4})/

    https://regex101.com/r/ohtTfO/2

    其中维护了列结构

    请注意,如果缺少第 3 列条目,则很可能没有
    弹出一个额外的\s{2,},所以你不能只说整个事情都是可选的
    因为它会将第 3 列变为空字符串,而不是空字符串。

    为了解决这个问题,我只使用了分支重置
    (?|\s{2,}(data)|()) 始终与第 3 列匹配
    如果不存在则将其设为空字符串...

    Formatted(为了方便使用)

     ( \d+ )                                  # (1)
     \s{2,} 
     (                                        # (2 start)
          [.a-zA-Z,'À-úÀ-ÿ()?\-/\d]+ 
          (?:
               \s? 
               [.a-zA-Z,'À-úÀ-ÿ()?\-/\d] 
          )*
     )                                        # (2 end)
     (?|
          \s{2,} 
          (                                        # (3 start)
               (?:
                    [.a-zA-Z,'À-úÀ-ÿ()?\-/\d]+ 
                    (?:
                         \s? 
                         [.a-zA-Z,'À-úÀ-ÿ()?\-/\d] 
                    )*
               )
          )                                        # (3 end)
       |  ( )                                      # (3)
     )
     \s{2,} 
     ( I | PF | MA )                          # (4)
     \s{2,} 
     ( I | PF | PL | LI | MA | CV | MJ )      # (5)
     \s{2,} 
     ( \w+ )                                  # (6)
     \s{2,} 
     ( \d+ , \d{4} )                          # (7)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-05-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多