Ruby从字符串数组中提取子字符串

时间:2015-02-25 06:24:32

标签: ruby substring

我有一个字符串数组。

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"]]

但我不想使用全局变量。有什么改进我的代码的建议吗?

4 个答案:

答案 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"]

正则表达式描述:

  1. \s - 任何空格字符,

  2. . - 任何字符.+ - 任何字符中的一个或多个() - 捕获内部的所有内容,因此(.+) - 捕获一个或多个字符

  3. 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后跟012或(|)b) 19之间的数字。
  • (?::...)中的第二个冒号表示将在另一个非捕获组中以冒号开头匹配。)
  • [0-5]{2}表示匹配两个({2})个字符,每个字符位于05之间。
  • {li> 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,这使得临时变量用于保存并尽可能找到本地结果。