在其他字符串之间是否有 ruby 方法到 select 字符串?
Is there ruby methods to select string between other strings?
我正在开始编程,我正在寻找一个程序来提取文本中两个单词之间包含的所有单词(以便将它们存储在变量中)
例如 "START" 和 "STOP":
"START 1 2 3 STOP 5 6 START 7 8 STOP 9 10"
我想存储在变量中:1 2 3 7 8
我开始用Ruby来做,你可以在下面的代码中看到,我目前的想法是将字符串"global"转换成一个数组,然后对string1和string2的位置进行编号;然后用初始数组#string1 + 1,… string2 -1 的值创建一个数组“string1”。
不幸的是,它只工作一次,因为 .index 函数只在第一次出现时工作...有更好的方法吗?
提前感谢您的帮助
text = "0 start 2 3 4 stop 6 7 start 9 10 stop 12"
start= text.split(' ')
a = start.index('start')
b = start.index('stop')
puts a
puts b
puts c = start[a+1,b-a-1].join(" ")
# returns
#1
#5
#2 3 4 ```
您并没有收到错误,codereview might be a better place to ask. But since you are new in the community, here is a regular expression with lookaround assertions 可以完成工作:
text = "0 start 2 3 4 stop 6 7 start 9 10 stop 12"
text.scan(/start ((?:(?!start).)*?) stop/).join(' ')
# => "2 3 4 9 10"
顺便说一句,在 Ruby 中测试正则表达式的好地方是 https://rubular.com/
希望对您有所帮助。
您可以从 scan
方法和正则表达式开始:
text = "0 start 2 3 4 stop 6 7 start 9 10 stop 12"
res1 = text.scan(/start\s*(.*?)\s*stop/) #[["2 3 4"], ["9 10"]]
res2 = res1.flatten #["2 3 4", "9 10"]
或没有中间变量:
res = text.scan(/start(.*?)stop/).flatten #["2 3 4", "9 10"]
解释:
扫描方法见https://apidock.com/ruby/String/scan。
正则表达式/start\s*(.*?)\s*stop/
是
的组合
- 开始
\s*
:任何space字符
(.*?)
:
(
和)
负责记住内容。
.
表示任意字符,*
表示重复(零个或多个字符),?
将结果限制为最短的可能性(详见下文)
\s*
:任何space字符
stop
结果是一个包含正则表达式匹配项的数组。正则表达式可以包含要检测的不同部分(多个 ()
对)。所以它是一个数组的数组。在我们的例子中,每个内部数组都有一个元素,所以你可以使用 flatten
来得到一个 'flat' 数组。
如果您不在正则表达式中使用 ?
,那么您会找到 2 3 4 stop 6 7 start 9 10
而不是较短的部分。
一行方法链
这是一种基于String#scan的方法:
text = "0 start 2 3 4 stop 6 7 start 9 10 stop 12"
text.scan(/\bstart\s+(.*?)\s+stop\b/i).flat_map { _1.flat_map &:split }
#=> ["2", "3", "4", "9", "10"]
这里的想法是:
提取不区分大小写的 start
和 stop
关键字之间的所有字符串段。
text.scan /\bstart\s+(.*?)\s+stop\b/i
#=> [["2 3 4"], ["9 10"]]
提取关键词之间用空格分隔的词。
[["2 3 4"], ["9 10"]].flat_map { _1.flat_map &:split }
#=> ["2", "3", "4", "9", "10"]
注意事项
上述方法的值得注意的注意事项包括:
- String#scan 创建嵌套数组,重复调用 Enumerable#flat_map 来处理它们并不像我希望的那样优雅。
\b
是零宽度断言,因此查找单词边界可能会导致 #scan 在结果中包含前导和尾随空格,然后需要由 String#strip or String#split 处理。
- 用
\s+
代替 \b
可以处理一些边缘情况,同时创建其他情况。
- 它没有做任何事情来防止不平衡的对,例如
"start 0 start 2 3 4 stop 6 stop"
.
对于简单的用例,String#scan 和经过调整的正则表达式可能就是您所需要的。您的输入和数据结构越多样化和不可预测,您的解析例程需要处理的边缘情况就越多。
选项使用数组:作为起点我建议使用Enumerable#slice_before after String#split
根据您的命令和停用词:
command = "START 1 2 3 STOP 5 6 START 7 8 STOP 9 10"
start = 'START'
stop = 'STOP'
你可以这样使用它:
grouped_cmd = command.split.slice_before { |e| [start, stop].include? e } # .to_a
#=> [["START", "1", "2", "3"], ["STOP", "5", "6"], ["START", "7", "8"], ["STOP", "9", "10"]]
然后你可以随意操作,例如:
grouped_cmd.select { |first, *rest| first == start }
#=> [["START", "1", "2", "3"], ["START", "7", "8"]]
或
grouped_cmd.each_with_object([]) { |(first, *rest), ary| ary << rest if first == start }
#=> [["1", "2", "3"], ["7", "8"]]
甚至
grouped_cmd.each_slice(2).map { |(start, *stt), (stop, *stp)| { start.downcase.to_sym => stt, stop.downcase.to_sym => stp } }
#=> [{:start=>["1", "2", "3"], :stop=>["5", "6"]}, {:start=>["7", "8"], :stop=>["9", "10"]}]
以此类推
我正在开始编程,我正在寻找一个程序来提取文本中两个单词之间包含的所有单词(以便将它们存储在变量中)
例如 "START" 和 "STOP": "START 1 2 3 STOP 5 6 START 7 8 STOP 9 10"
我想存储在变量中:1 2 3 7 8
我开始用Ruby来做,你可以在下面的代码中看到,我目前的想法是将字符串"global"转换成一个数组,然后对string1和string2的位置进行编号;然后用初始数组#string1 + 1,… string2 -1 的值创建一个数组“string1”。 不幸的是,它只工作一次,因为 .index 函数只在第一次出现时工作...有更好的方法吗?
提前感谢您的帮助
text = "0 start 2 3 4 stop 6 7 start 9 10 stop 12"
start= text.split(' ')
a = start.index('start')
b = start.index('stop')
puts a
puts b
puts c = start[a+1,b-a-1].join(" ")
# returns
#1
#5
#2 3 4 ```
您并没有收到错误,codereview might be a better place to ask. But since you are new in the community, here is a regular expression with lookaround assertions 可以完成工作:
text = "0 start 2 3 4 stop 6 7 start 9 10 stop 12"
text.scan(/start ((?:(?!start).)*?) stop/).join(' ')
# => "2 3 4 9 10"
顺便说一句,在 Ruby 中测试正则表达式的好地方是 https://rubular.com/
希望对您有所帮助。
您可以从 scan
方法和正则表达式开始:
text = "0 start 2 3 4 stop 6 7 start 9 10 stop 12"
res1 = text.scan(/start\s*(.*?)\s*stop/) #[["2 3 4"], ["9 10"]]
res2 = res1.flatten #["2 3 4", "9 10"]
或没有中间变量:
res = text.scan(/start(.*?)stop/).flatten #["2 3 4", "9 10"]
解释:
扫描方法见https://apidock.com/ruby/String/scan。
正则表达式/start\s*(.*?)\s*stop/
是
- 开始
\s*
:任何space字符(.*?)
:(
和)
负责记住内容。.
表示任意字符,*
表示重复(零个或多个字符),?
将结果限制为最短的可能性(详见下文)
\s*
:任何space字符stop
结果是一个包含正则表达式匹配项的数组。正则表达式可以包含要检测的不同部分(多个 ()
对)。所以它是一个数组的数组。在我们的例子中,每个内部数组都有一个元素,所以你可以使用 flatten
来得到一个 'flat' 数组。
如果您不在正则表达式中使用 ?
,那么您会找到 2 3 4 stop 6 7 start 9 10
而不是较短的部分。
一行方法链
这是一种基于String#scan的方法:
text = "0 start 2 3 4 stop 6 7 start 9 10 stop 12"
text.scan(/\bstart\s+(.*?)\s+stop\b/i).flat_map { _1.flat_map &:split }
#=> ["2", "3", "4", "9", "10"]
这里的想法是:
提取不区分大小写的
start
和stop
关键字之间的所有字符串段。text.scan /\bstart\s+(.*?)\s+stop\b/i #=> [["2 3 4"], ["9 10"]]
提取关键词之间用空格分隔的词。
[["2 3 4"], ["9 10"]].flat_map { _1.flat_map &:split } #=> ["2", "3", "4", "9", "10"]
注意事项
上述方法的值得注意的注意事项包括:
- String#scan 创建嵌套数组,重复调用 Enumerable#flat_map 来处理它们并不像我希望的那样优雅。
\b
是零宽度断言,因此查找单词边界可能会导致 #scan 在结果中包含前导和尾随空格,然后需要由 String#strip or String#split 处理。- 用
\s+
代替\b
可以处理一些边缘情况,同时创建其他情况。 - 它没有做任何事情来防止不平衡的对,例如
"start 0 start 2 3 4 stop 6 stop"
.
对于简单的用例,String#scan 和经过调整的正则表达式可能就是您所需要的。您的输入和数据结构越多样化和不可预测,您的解析例程需要处理的边缘情况就越多。
选项使用数组:作为起点我建议使用Enumerable#slice_before after String#split
根据您的命令和停用词:
command = "START 1 2 3 STOP 5 6 START 7 8 STOP 9 10"
start = 'START'
stop = 'STOP'
你可以这样使用它:
grouped_cmd = command.split.slice_before { |e| [start, stop].include? e } # .to_a
#=> [["START", "1", "2", "3"], ["STOP", "5", "6"], ["START", "7", "8"], ["STOP", "9", "10"]]
然后你可以随意操作,例如:
grouped_cmd.select { |first, *rest| first == start }
#=> [["START", "1", "2", "3"], ["START", "7", "8"]]
或
grouped_cmd.each_with_object([]) { |(first, *rest), ary| ary << rest if first == start }
#=> [["1", "2", "3"], ["7", "8"]]
甚至
grouped_cmd.each_slice(2).map { |(start, *stt), (stop, *stp)| { start.downcase.to_sym => stt, stop.downcase.to_sym => stp } }
#=> [{:start=>["1", "2", "3"], :stop=>["5", "6"]}, {:start=>["7", "8"], :stop=>["9", "10"]}]
以此类推