我试图匹配一些可以是一行或两行的文本。我希望能够以有效的方式处理这两种情况。文本字符串将一致地格式化,并包含多个选项卡。我正在尝试用红宝石做比赛。案文如下:
单行:
#3 Hello Stormy Scratched - Reason Unavailable 11:10 AM ET
两行:
#3 Hello Stormy Scratched - Reason Unavailable 11:10 AM ET
Scratch Reason - Reason Unavailable changed to Trainer 2:19 PM ET
我必须在这里使用空格来格式化字符串,但实际的文本使用制表符来分隔各个部分:数字和名称,Scratched以及原因和时间。
示例输出:
一行:#3 Hello Stormy Scratched - 原因在东部时间上午11点10分不可用
两行#3 Hello Stormy Scratched - Reason Unavailable更改为Trainer 2:19 PM
注意:理想情况下,两行输出将包含第一行的数字和名称。
我能够构建一个匹配各个部分的表达式,但是选项卡,第二行以及在两行输出上具有数字和马名称的要求给我带来了麻烦。
答案 0 :(得分:2)
你不需要花哨的正则表达来做你想做的事情,你只需要知道如何去做。
Ruby的Enumerable有一个名为slice_before
的方法,它采用正则表达式,用于确定数组中的哪些元素组合在一起。数组继承自Enumerable。例如:
text = '#3 Hello Stormy Scratched - Reason Unavailable 11:10 AM ET
#3 Hello Stormy Scratched - Reason Unavailable 11:10 AM ET
Scratch Reason - Reason Unavailable changed to Trainer 2:19 PM ET
'
data = text.split("\n").slice_before(/\A\S/).to_a
require 'pp'
pp data
输出:
[["#3\tHello Stormy\tScratched\t-\tReason Unavailable\t\t\t11:10 AM ET"],
["#3\tHello Stormy\tScratched\t-\tReason Unavailable\t\t\t11:10 AM ET",
"\t\t\tScratch\tReason\t-\tReason Unavailable changed to Trainer\t2:19 PM ET"]]
换句话说,通过拆分"\n"
上的文本创建的数组按不以空格开头的行分组,即模式/\A\S/
。所有单行都在单独的子阵列中。作为前一行的延续的行与该行分组。
如果您正在从磁盘读取文件,则可以使用IO.readlines
将文件作为数组读取,从而无需拆分文件。
如果需要,您可以进一步处理该数组,使用以下内容重建行和延续行:
data = text.split("\n").slice_before(/\A\S/).map{ |i| i.join("\n") }
将data
变为:
["#3\tHello Stormy\tScratched\t-\tReason Unavailable\t\t\t11:10 AM ET",
"#3\tHello Stormy\tScratched\t-\tReason Unavailable\t\t\t11:10 AM ET\n\t\t\tScratch\tReason\t-\tReason Unavailable changed to Trainer\t2:19 PM ET"]
如果您需要将每一行拆分为其组成字段,请使用split("\t")
。如何在子阵列中执行此操作只是为了练习,但我会涉及map
。
编辑:
...我喜欢你的解决方案,但我正在为slice_before获取未定义的方法。
试试这个:
require 'pp'
require 'rubygems'
class Array
unless Array.respond_to?(:slice_before)
def slice_before(pat)
result = []
temp_result = []
self.each do |i|
if (temp_result.empty?)
temp_result << i
next
end
if i[pat]
result << temp_result
temp_result = []
end
temp_result << i
end
result << temp_result
end
end
end
打电话给:
ary = [
'#3 Hello Stormy Scratched - Reason Unavailable 11:10 AM ET',
'#3 Hello Stormy Scratched - Reason Unavailable 11:10 AM ET',
' Scratch Reason - Reason Unavailable changed to Trainer 2:19 PM ET',
]
pp ary.slice_before(/\A\S/)
看起来像:
[
["#3 Hello Stormy Scratched - Reason Unavailable 11:10 AM ET"],
["#3 Hello Stormy Scratched - Reason Unavailable 11:10 AM ET",
" Scratch Reason - Reason Unavailable changed to Trainer 2:19 PM ET"]
]
答案 1 :(得分:1)
如果您可以假设'#'字符没有出现在字符串中的任何其他位置,那么它会相当简化。那么类似的东西应该这样做:
/^#[^#]*/m
另一种更通用的方法是匹配以#开头的第一行,以及以空格或制表符开头的任何行:
/^#.*?$(\n^[ \t].*?$)*/m
如果该行并非始终以#开头,则可以将其替换为[^ \t]
(不是空格或制表符)。
答案 2 :(得分:1)
RE的乐趣!这很糟糕,但那里有几种不同类型的匹配策略。
# Two-line example
s = <<-EOS
#3\tHello Stormy\t\tScratched - Reason Unavailable\t\t\t11:10 AM ET\t
\t\t\tScratch Reason - Reason Unavailable changed to Trainer\t2:19 PM ET
EOS
# allow leading/trailing whitespace, get the number, name, last reason and time
s =~ /\A\s*(#\d)\t+([^\t]+)(?:\t+.*)?(?:\t+(.*))\t+(\d+:\d+ (?:AM|PM) ET)\s*\Z/m
# ["#3", "Hello Stormy", "Scratch Reason - Reason Unavailable changed to Trainer", "2:19 PM ET"]
a = $1, $2, $3, $4
注意:这假设您匹配的字符串中只有一条消息 注意:未针对单行案例进行测试:)