用于匹配名称和可选值的正则表达式

Regex to match names and optional values

我有一个 PHP 应用程序,它与支付处理器接口以处理信用卡。有时,来自处理器的 post 响应失败(例如矩阵中的暂时故障),我们没有收到付款的自动通知。在这些情况下,我们会回退到从始终发送的确认电子邮件中输入数据。我希望我的代码解析出电子邮件的文本以获取数据,这似乎是 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 : 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 :

我想要这样的输出:

[
    [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] =>
]

重要注意事项:

我最接近的是:

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

但这会产生如下输出:

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

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

解决方案一:

我按照以下方式调整了你的正则表达式:

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

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

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

方案二:检查link: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]+(?= ))?) ?

局限性:space 字符仅可用于 phone 数字,下划线仅可用于邮件地址。

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

(\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+ :|$))?

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