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"]
正则表达式描述:
\s
- 任何空白字符,
.
- 任何字符,.+
- 一个或多个任何字符,()
- 捕获里面的所有内容,所以 (.+)
- 捕获一个或多个字符
a?
表示 a
中的零个或一个
您也可以使用这样的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
后跟 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."
我们希望select“上午 10 点”而不是“上午 9:30”。为此,我们需要额外的假设。例如,我们可能假设时间前面有单词 "at",并且 "at" 紧跟在酒店名称之后:
Hotel KingsMen at 10am
或
Kingsmen hotel at 10:15 am
我们可以使用一个相当复杂的正则表达式来提取这里的时间,或者我们可以先在字符串中找到酒店名称和它的位置,然后在后面立即查找时间。
我有一个字符串数组。
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"]
正则表达式描述:
\s
- 任何空白字符,.
- 任何字符,.+
- 一个或多个任何字符,()
- 捕获里面的所有内容,所以(.+)
- 捕获一个或多个字符a?
表示a
中的零个或一个
您也可以使用这样的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
后跟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."
我们希望select“上午 10 点”而不是“上午 9:30”。为此,我们需要额外的假设。例如,我们可能假设时间前面有单词 "at",并且 "at" 紧跟在酒店名称之后:
Hotel KingsMen at 10am
或
Kingsmen hotel at 10:15 am
我们可以使用一个相当复杂的正则表达式来提取这里的时间,或者我们可以先在字符串中找到酒店名称和它的位置,然后在后面立即查找时间。