多行/未知数据正则表达式查询

时间:2018-01-23 01:12:49

标签: ruby regex regex-lookarounds

我有一个文本数据集,我需要解析为多个“组”。这些组是数字标识符。我遇到的问题是,此数据集中的每个“CUSTOMER”都有一组未知的数据,并且在用于识别它们的最初两行之后还有一些额外的行。

例如:

CUSTOMER|100000|Last|First|20171200-000000|20171200-000000|||||||||||||N|||
PAYMENTS|||1234|1234|20171200-000000|20171200-000000||||||||||||1||||
REBOUND||||||||||
INFO||||||||||
CUSTOMER|100001|Last|First|20171200-000000|20171200-000000|||||||||||||N|||
PAYMENTS|||1234|1234|20171200-000000|20171200-000000||||||||||||142||||
INFO||||||||||
CUSTOMER|100002|Last|First|20171200-000000|20171200-000000|||||||||||||N|||
PAYMENTS|||1234|1234|20171200-000000|20171200-000000||||||||||||1||||
INFO||||||||||
PRE||||||||||
INFO||||||||||

所以在这个例子中,我想删除值为142的CUSTOMERS。在CUSTOMER 100001末尾的PAYMENTS行中可以看到142的值。

我一直在使用Ruby,但我愿意用它来完成任务。所以最初,我写了这个:

CUSTOMER\|.*\|142\|.+?(?=\nCUSTOMER)

使用m标志,查找CUSTOMER,然后查找| 142 |然后做一个积极的前瞻,直到它再次见到CUSTOMER。这个问题是,正则表达式识别出找到的初始CUSTOMER(100000)并继续向下查找文件,直到找到| 142 |。然后停下来我需要的是正则表达式在它看到之前再次看到CUSTOMER这个词时停止| 142 |

我试图在

之后添加一个否定的向前看
 CUSTOMER\|.*(?!CUSTOMER)

但这仍然给了我完整的结果。我不确定贪婪的角色匹配有多么负面的前瞻,但我认为它会说“如果在某些时候我发现这个失败点,这不是匹配”

从那时起,我尝试了一些其他的解决方案,用这个解决了一下:

(?!.*(?:\|142\|))

但我也无处可去。我仍然坚持阻止第一个CUSTOMER分组到第二个。想法?

2 个答案:

答案 0 :(得分:0)

这是一种似乎有效的模式,至少在演示中测试时是这样的:

CUSTOMER((?!\nCUSTOMER).)*?\|142\|.*?(?=\nCUSTOMER)

以下是解释:

((?!\nCUSTOMER).)*?   Using a tempered dot, consume anything so long as
                      we do not encounter `\nCUSTOMER` on the next line
\|142\|               Match `|142|` somewhere inside the line
.*?(?=\nCUSTOMER)     Then consume the rest of the line up to CUSTOMER

Demo

从演示中注意到我的正则表达式存在缺陷。如果要删除的CUSTOMER恰好是文件中的最后一个,那么正则表达式会错过它,因为之后将没有开头CUSTOMER

希望这对你来说至少是一个很好的起点。

答案 1 :(得分:0)

我会分两步而不是一步。这使解决方案更容易理解,测试和维护,并且可能is a wash效率更高。

首先,让str保存从文件中读取的字符串。

str =<<BITTER_END
CUSTOMER|100000|Last|First|20171200-000000|20171200-000000|||||||||||||N|||
PAYMENTS|||1234|1234|20171200-000000|20171200-000000||||||||||||1||||
REBOUND||||||||||
INFO||||||||||
CUSTOMER|100001|Last|First|20171200-000000|20171200-000000|||||||||||||N|||
PAYMENTS|||1234|1234|20171200-000000|20171200-000000||||||||||||142||||
INFO||||||||||
CUSTOMER|100002|Last|First|20171200-000000|20171200-000000|||||||||||||N|||
PAYMENTS|||1234|1234|20171200-000000|20171200-000000||||||||||||1||||
INFO||||||||||
PRE||||||||||
INFO||||||||||
BITTER_END

第一步使用正则表达式将str拆分为一个字符串数组,每个字符串对应一个客户。

r = /
    (?<=^)          # Match a beginning of line anchor in a positive lookbehind
    (?=CUSTOMER\|)  # Match 'CUSTOMER|' in a positive lookahead
    /x              # Free-spacing regex definition mode

请注意,当与String#split一起使用时,此正则表达式会破坏字符之间的字符串,即在换行符之后和字符串'CUSTOMER|'之前。

我使用自由间隔模式使正则表达式自我记录。通常,它将被写成

r = /(?<=^)(?=CUSTOMER\|)/ 

现在让我们将str分成几组。

a0 = str.split(r)

为了更容易看到返回值,让我分别打印a0的每个元素。

a0.size.times { |i| puts "\n#{a0[i]}" } 

CUSTOMER|100000|Last|First|20171200-000000|20171200-000000|||||||||||||N|||
PAYMENTS|||1234|1234|20171200-000000|20171200-000000||||||||||||1||||
REBOUND||||||||||
INFO||||||||||

CUSTOMER|100001|Last|First|20171200-000000|20171200-000000|||||||||||||N|||
PAYMENTS|||1234|1234|20171200-000000|20171200-000000||||||||||||142||||
INFO||||||||||

CUSTOMER|100002|Last|First|20171200-000000|20171200-000000|||||||||||||N|||
PAYMENTS|||1234|1234|20171200-000000|20171200-000000||||||||||||1||||
INFO||||||||||
PRE||||||||||
INFO||||||||||

第二步删除包含字符串a0的{​​{1}}元素。

'|142|'

a1.size.times {| i |把“\ n#{a1 [i]}”}

a1 = a0.reject { |s| s.include?('|142|') }

我们当然会以通常的方式将这两个步骤链接起来。

CUSTOMER|100000|Last|First|20171200-000000|20171200-000000|||||||||||||N|||
PAYMENTS|||1234|1234|20171200-000000|20171200-000000||||||||||||1||||
REBOUND||||||||||
INFO||||||||||

CUSTOMER|100002|Last|First|20171200-000000|20171200-000000|||||||||||||N|||
PAYMENTS|||1234|1234|20171200-000000|20171200-000000||||||||||||1||||
INFO||||||||||
PRE||||||||||
INFO||||||||||

如果返回值是字符串,则将str.split(r).reject { |s| s.include?('|142|') } 加到上面的末尾。 (我在问题陈述中不清楚这一点。)