我有一个文本数据集,我需要解析为多个“组”。这些组是数字标识符。我遇到的问题是,此数据集中的每个“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分组到第二个。想法?
答案 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
从演示中注意到我的正则表达式存在缺陷。如果要删除的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|') }
加到上面的末尾。 (我在问题陈述中不清楚这一点。)