我有一个字符串数组。
irb(main):009:0* str_arr
=> ["hello how are you?", "I am fine.What are you doing?", "Hey, I am having a haircut. See you at Hotel KingsMen at 10 am."]
我正试图从中提取一些信息。酒店的名称和时间。
irb(main):010:0> q = str_arr[2].scan(/(.*)Hotel(.*)at(.*)\./)
=> [["Hey, I am having a haircut. See you at ", " KingsMen ", " 10 am"]]
问题是我无法将索引修复为2.我需要这样的东西:
irb(main):023:0> str_arr.each { |str| $res = str.scan(/(.*)Hotel(.*)at(.*)\./) }
=> ["hello how are you?", "I am fine.What are you doing?", "Hey, I am having a haircut. See you at Hotel KingsMen at 10 am."]
irb(main):024:0> $res
=> [["Hey, I am having a haircut. See you at ", " KingsMen ", " 10 am"]]
但我不想使用全局变量。有什么改进我的代码的建议吗?
答案 0 :(得分:3)
s = ["hello how are you?", "I am fine.What are you doing?", "Hey, I am having a haircut. See you at Hotel KingsMen at 10 am."]
s.join.scan(/Hotel\s(.+)?\sat\s(.+)?\./).flatten
#=> ["KingsMen", "10 am"]
正则表达式描述:
\s
- 任何空格字符,
.
- 任何字符.+
- 任何字符中的一个或多个()
- 捕获内部的所有内容,因此(.+)
- 捕获一个或多个字符
a?
表示零{或a
答案 1 :(得分:2)
这是你的数组:
arr = ["hello how are you?",
"I am fine. What are you doing?",
"Hey, I am having a haircut. See you at Hotel KingsMen at 10 am."]
第一步是将元素连接成一个字符串。我已选择使用空格作为分隔符,但您可以使用其他内容:
str = arr.join(' ')
#=> "hello how...doing? Hey,...haircut. See you at Hotel KingsMen at 10 am."
不失一般性,让我们假设这个字符串是以下之一:
str1 = "See you at Hotel KingsMen at 10 am."
str2 = "See you at 10:15am at Kingsmen hotel on Bloor Street."
哪家酒店?
让我们先来看看如何获得酒店的名称。我们想要一个适用于这两个字符串的方法。我们假设酒店的名称只有两个单词,其中一个单词是" hotel",但我们不知道这两个单词中的哪一个是第一个,我们允许&#34 ;酒店"以大写或小写字母开头。
我们在str1
看到它可能是"在酒店"或者" Hotel KingsMen",以及str2
它可能是"金斯敦酒店"或"酒店在"。通过合理假设除了" hotel"之外的单词,可以获得正确的结果。资本化。
以下是一种方法:
def hotel(str)
str[/\b[hH]otel\s+\K[A-Z][a-zA-Z]*|[A-Z][a-zA-Z]*(?=\s[Hh]otel\b)/]
end
hotel(str1) #=> "KingsMen"
hotel(str2) #=> "Kingsmen"
下面:
\b
是一个(零宽度)分词\K
表示匹配之前的内容,但不会将其包含在返回的匹配项中。|
表示匹配之前或之后的内容。(?=\s[Hh]otel\b)
是一个("零宽度")正向前瞻,它表示必须紧接着之前的内容,但不是比赛的一部分。现在几点?
在这里,我们必须假设时间的表达方式。应该"中午"," 1100小时"和" 14:21"可能吗?好的,这只是一个练习,所以让我们假设它是一个12小时的时钟,有几小时甚至几分钟,但没有秒。
我们可以使用以下正则表达式来提取该信息:
def time(str)
str[/\b(?:1[012]|[1-9])(?::[0-5]{2})?\s?(?:[ap]m?)/i]
end
time(str1) #=> "10 am"
time(str2) #=> "10:15am"
下面:
1[012]|[1-9]
说匹配a)1
后跟0
,1
或2
或(|
)b) 1
和9
之间的数字。(?::...)
中的第二个冒号表示将在另一个非捕获组中以冒号开头匹配。)[0-5]{2}
表示匹配两个({2}
)个字符,每个字符位于0
和5
之间。i
/i
表示无视案例。
假设我们现在有:
str3 = "I'm leaving at 9:30 am, so I'll see you at Hotel KingsMen at 10 am."
我们想选择"上午10点"而不是"上午9:30"。为此我们需要额外的假设。例如,我们可以假设时间之前是单词" at"那" at"出现在酒店名称后面:
Hotel KingsMen at 10am
或
Kingsmen hotel at 10:15 am
我们可以使用相当复杂的正则表达式来提取时间,或者我们可以先找到酒店名称及其在字符串中的位置,然后立即查找时间。
答案 2 :(得分:0)
你也可以使用像这样的选择方法
[
"hello how are you?", "I am fine.What are you doing?",
"Hey, I am having a haircut. See you at Hotel KingsMen at 10 am."
].select{|str| str =~ /Hotel\s(.+)?\sat\s(.+)?\./}
#=> ["Hey, I am having a haircut. See you at Hotel KingsMen at 10 am."]
答案 3 :(得分:0)
如果您想保留当前的解决方案并且不想使用全局变量,那么我建议您使用' reduce'方法:
str = ["hello how are you?", "I am fine.What are you doing?", "Hey, I am having a haircut. See you at Hotel KingsMen at 10 am."]
str.reduce([]) do |res, s|
res == [] ? s.scan(/(.*)Hotel(.*)at(.*)\./) : res
end
# => [["Hey, I am having a haircut. See you at ", " KingsMen ", " 10 am"]]
IMO,这使得临时变量用于保存并尽可能找到本地结果。