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

Ruby extract substring from an array of strings

我有一个字符串数组。

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

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

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

  4. 中的零个或一个

您也可以使用这样的select方法

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

如果您想保留当前的解决方案并且不想使用全局变量,那么我建议您使用 '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,这使得用于保存和查找结果的临时变量尽可能本地化。

这是你的数组:

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

第一步是将元素连接成一个字符串。我选择使用 space 作为分隔符,但您可以使用其他东西:

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",但我们不知道这两个词中哪个在前,所以我们允许"hotel"开头大写或小写字母。

我们在str1中看到它可能是"at Hotel"或"Hotel KingsMen",而在str2中它可能是"Kingston hotel"或"hotel on" .正确的结果是通过合理假设 "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) 是 ("zero-width") 正向前瞻,表示必须紧跟在之前的内容之后,但不是匹配的一部分。

什么时候?

这里我们必须对时间的表达方式做一个假设。 "noon"、“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之间。
  • /i中的
  • i表示不区分大小写。

假设现在我们有:

str3 = "I'm leaving at 9:30 am, so I'll see you at Hotel KingsMen at 10 am."  

我们希望select“上午 10 点”而不是“上午 9:30”。为此,我们需要额外的假设。例如,我们可能假设时间前面有单词 "at",并且 "at" 紧跟在酒店名称之后:

Hotel KingsMen at 10am

Kingsmen hotel at 10:15 am

我们可以使用一个相当复杂的正则表达式来提取这里的时间,或者我们可以先在字符串中找到酒店名称和它的位置,然后在后面立即查找时间。