【问题标题】:Regex to match names and optional values正则表达式匹配名称和可选值
【发布时间】:2018-10-11 02:37:24
【问题描述】:

我有一个 PHP 应用程序,它与支付处理器接口以处理信用卡。有时,来自处理器的后响应失败(例如矩阵中的瞬间故障),我们没有收到付款的自动通知。在这些情况下,我们回退到从始终发送的确认电子邮件中输入数据。我希望我的代码解析出电子邮件的文本以获取数据,这似乎是 preg_match_all 的完美用例。问题是电子邮件格式错误:它以 name : value 对出现,但它们都在一行上,而且值通常是空白的,这让我很困惑。

我非常擅长正则表达式基础知识(量词、分组、字符类、锚点、修饰符),但对前瞻和后向引用真的没有经验,而且我也不知道它们是否能提供帮助。

示例数据可能看起来像这样(同样,这些都在一行上,只是为了便于阅读而进行了包装):

bypass_first_page : x_company : x_cust_id : 12345 x_customer_ip : x_customer_tax_id:x_description:98765 x_duty:x_email_customer: an_example@example.com x_fax : x_footer_email_receipt : x_fp_hash : 747ffddfe4e106a9c67363ebff996ad x_fp_timestamp:1525100766 x_invoice_num : R000098765 x_login : MY-LOGIN-ID x_logo_url : x_merchant_email : x_method : x_phone : (416) 555-1212 x_po_num : x_receipt_link_method:获取 x_reference_3:1234 x_relay_response: 真 x_relay_url :

我想要如下所示的输出:

[
    [bypass_first_page] =>
    [x_company] =>
    [x_cust_id] => 12345
    [x_customer_ip] =>
    [x_customer_tax_id] =>
    [x_description] => 98765
    [x_duty] =>
    [x_email_customer] => an_example@example.com
    [x_fax] =>
    [x_footer_email_receipt] =>
    [x_fp_hash] => 747ffeddfe4e106a9c67363ebff996ad
    [x_fp_timestamp] => 1525100766
    [x_invoice_num] => R000098765
    [x_login] => MY-LOGIN-ID
    [x_logo_url] =>
    [x_merchant_email] =>
    [x_method] =>
    [x_phone] => (416) 555-1212
    [x_po_num] =>
    [x_receipt_link_method] => GET
    [x_reference_3] => 1234
    [x_relay_response] => TRUE
    [x_relay_url] =>
]

需要注意的重要事项:

  • 字段名称大多(但不限于)以 x_ 开头。如果只能找到需要这个的解决方案,它可能是可行的。
  • 字段名称​​没有包含空格。
  • 某些字段名称中包含数字。
  • 可以包含空格(例如电话号码)和下划线(例如电子邮件地址)。
  • 当没有值时,冒号和下一个字段名之间只有一个空格。

我最接近的是:

/([\w\d_]+) ?: ([^:]+)/

但这会产生如下输出:

[
    [bypass_first_page] => x_company
    [x_cust_id] => 12345 x_customer_ip
    [x_customer_tax_id] => x_description
    ...
]

正如您从this regex101 link 中看到的那样,这是失败的,因为存在与任何内容都不匹配的冒号,并且字段名称最终出现在值中(单独或与实际值连接)。我觉得如果有一个修饰符要求匹配整个字符串,或者锚点以某种方式指示一个匹配必须从前一个匹配结束的地方开始,这可以很容易地解决这个问题,但我找不到任何提及任何地方都有这样的事情。可能只是我不知道那东西叫什么?

【问题讨论】:

  • 支付网关是否不提供用于核对支付的 API? 12345x_customer_ip 之间缺少 : 是您的示例数据中的错误,还是在回复电子邮件中出现这样的错误?使用explode(' x_', $string); 之类的东西可能会让你更幸运
  • @fubar,在12345x_customer_ip 之间没有:: 因为12345 是前一个字段的值。值之后没有:s,只是在名称和值之间。关于 API 的好主意,我会看看是否存在。
  • 好的,知道了。干杯。

标签: php regex pcre


【解决方案1】:

我找到的最简单的解决方案(到目前为止)是这样的:

(\w+) : ?(.*?)(?= ?\w+ :|$)

Demo

最后,按照 Allen 的建议在末尾添加 ? 会使输出更好。

(\w+) : ?(.*?)(?= ?\w+ :|$) ?

输出:

[0] => Array
    (
        [0] => bypass_first_page : 
        [1] => x_company : 
        [2] => x_cust_id : 12345
        [3] => x_customer_ip : 
        [4] => x_customer_tax_id : 
        [5] => x_description : 98765
        [6] => x_duty : 
        [7] => x_email_customer : an_example@example.com
        [8] => x_fax : 
        [9] => x_footer_email_receipt : 
        [10] => x_fp_hash : 747ffeddfe4e106a9c67363ebff996ad
        [11] => x_fp_timestamp : 1525100766
        [12] => x_invoice_num : R000098765
        [13] => x_login : MY-LOGIN-ID
        [14] => x_logo_url : 
        [15] => x_merchant_email : 
        [16] => x_method : 
        [17] => x_phone : (416) 555-1212
        [18] => x_po_num : 
        [19] => x_receipt_link_method : GET
        [20] => x_reference_3 : 1234
        [21] => x_relay_response : TRUE
        [22] => x_relay_url :
    )

[1] => Array
    (
        [0] => bypass_first_page
        [1] => x_company
        [2] => x_cust_id
        [3] => x_customer_ip
        [4] => x_customer_tax_id
        [5] => x_description
        [6] => x_duty
        [7] => x_email_customer
        [8] => x_fax
        [9] => x_footer_email_receipt
        [10] => x_fp_hash
        [11] => x_fp_timestamp
        [12] => x_invoice_num
        [13] => x_login
        [14] => x_logo_url
        [15] => x_merchant_email
        [16] => x_method
        [17] => x_phone
        [18] => x_po_num
        [19] => x_receipt_link_method
        [20] => x_reference_3
        [21] => x_relay_response
        [22] => x_relay_url
    )

[2] => Array
    (
        [0] => 
        [1] => 
        [2] => 12345
        [3] => 
        [4] => 
        [5] => 98765
        [6] => 
        [7] => an_example@example.com
        [8] => 
        [9] => 
        [10] => 747ffeddfe4e106a9c67363ebff996ad
        [11] => 1525100766
        [12] => R000098765
        [13] => MY-LOGIN-ID
        [14] => 
        [15] => 
        [16] => 
        [17] => (416) 555-1212
        [18] => 
        [19] => GET
        [20] => 1234
        [21] => TRUE
        [22] => 
    )

我做了更多测试,认为这应该符合要求。

PS:我想到的第一个解决方案是this

(?:^| )(\w+) : ?(?!\w+ : )(?:(.*?)(?= \w+ :|$))?

它有点冗长,但可能对你也有帮助。

【讨论】:

  • 很好的答案和漂亮的正则表达式+1!!!感人的!顺便说一句,你不应该在它的末尾添加`?`吗? regex101.com/r/qLrEXM/1
  • 这看起来很完美!如果您认为可以稍微解释一下它是如何工作的,那将是一个奖励。
【解决方案2】:

解决方案 1:

我已通过以下方式调整了您的正则表达式:

(\w+|x_[^: ]*) ?:( ((?!x_|\()[^:() ]*|(?:(\d*[)( -])*\d+))?)? ?

它并不完美,但它在您的示例中运行良好,您可以在以下位置看到: https://regex101.com/r/tTr4lG/2

请注意,它还具有x_ 起始限制。

解决方案2:查看链接:https://regex101.com/r/tTr4lG/3

已删除起始x_ 限制!

(?<= |^)(([\w\d_]+) : ([A-Za-z0-9-]+(?= )|(\d*[)( -])*\d+|[A-Za-z0-9-_.]+@[A-Za-z0-9-_.]+\.[A-Za-z]+(?= ))?) ?

限制:空格字符只能用于电话号码,下划线只能用于邮件地址。

【讨论】:

  • 你有什么方法可以让它工作而不依赖以 x_ 开头的字段名吗?根据@fubar 回答的讨论。
  • @GregSchmidt:看看我的第二个答案
  • 我认为有两个问题:a)在最后一个冒号之后有一个额外的空格,b)这个解决方案假设特定的字段/值格式,这不是绝对必要的。检查我的解决方案。
猜你喜欢
  • 2016-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-14
  • 2020-06-24
  • 1970-01-01
相关资源
最近更新 更多