我很努力从iOS应用发送的字符串中提取ID和Answer值。在以下示例中,我有四个ID和四个Answers需要提取。从长字符串中提取值
s = "ID:1_Answer1_ID:2_Answer2_ID:3_AnswerRandom_ID:789_Answer3.5"
IDs_array = [1,2,3,789]
Answers_array = [Answer1,Answer2,AnswerRandom,Answer3.5]
感谢任何帮助或建议。
我很努力从iOS应用发送的字符串中提取ID和Answer值。在以下示例中,我有四个ID和四个Answers需要提取。从长字符串中提取值
s = "ID:1_Answer1_ID:2_Answer2_ID:3_AnswerRandom_ID:789_Answer3.5"
IDs_array = [1,2,3,789]
Answers_array = [Answer1,Answer2,AnswerRandom,Answer3.5]
感谢任何帮助或建议。
ids, answers = s.scan(/ID:(\d+)_([^_]+)/).transpose
正则表达式的想法是:
ID:
(\d+)
_
([^_]+)
String#scan
与一对阵列[id, answer]
的返回的数组的序列,因此,我们转置它来获得两个数组 - 一个与IDS和一个与答案。然后我们使用多个分配将解压外部数组。
请澄清一下您的问题。
ID和答案需要匹配吗?他们总是成对吗? "_"
字符是否始终用作分隔符(这意味着答案应该被编码)?总是格式:
"ID:#{id_mumber}_#{answer in text}"
... "_"
... *
我将承担回答所有的问题,我问的是“是的”,但如果我错了,请编辑您的问题,给我留言 - 我会编辑答案。
s = "ID:1_Answer1_ID:2_Answer2_ID:3_AnswerRandom_ID:789_Answer3.5"
answer_hash = {}
tmp = s.split('_')
answer_hash[tmp.shift[3..-1].to_i] = tmp.shift while tmp[0]
answer_hash # => {1=>"Answer1", 2=>"Answer2", 3=>"AnswerRandom", 789=>"Answer3.5"}
answer_hash.keys # => [1, 2, 3, 789]
answer_hash.values # => ["Answer1", "Answer2", "AnswerRandom", "Answer3.5"]
编辑
我使用正则表达式爱@ NDN的答案......这是清晰的,但可能会更短字符串会比较慢。
这里是我的机器上的基准 - 它们表明,在性能上的差异主要表现为较短的ID字符串:
s = "ID:1_Answer1_ID:2_Answer2_ID:3_AnswerRandom_ID:789_Answer3.5"
puts Benchmark.measure {100_000.times {answer_hash = {}; tmp = s.split('_'); answer_hash[tmp.shift[3..-1].to_i] = tmp.shift while tmp[0] } }
# ### Short string using str
# => 0.280000 0.000000 0.280000 ( 0.286917)
puts Benchmark.measure {100_000.times {ids, answers = *s.scan(/(?<=ID:)(\d+)_([^_]+)/).transpose } }
# ### Short string using string.scan Regexp
# => 0.590000 0.000000 0.590000 ( 0.595052)
s = []
100.times {|i| s << ("ID:#{i}_Answer#{i}") }
s = s.join('_')
puts Benchmark.measure {100_000.times {answer_hash = {}; tmp = s.split('_'); answer_hash[tmp.shift[3..-1].to_i] = tmp.shift while tmp[0] } }
# ### Medium string using string.split
# => 7.180000 0.010000 7.190000 ( 7.213266)
puts Benchmark.measure {100_000.times {ids, answers = *s.scan(/(?<=ID:)(\d+)_([^_]+)/).transpose } }
# ### Medium string using string.scan Regexp
# => 8.860000 0.020000 8.880000 ( 8.888352)
s = []
1000.times {|i| s << ("ID:#{i}_Answer#{i}") }
s = s.join('_')
puts Benchmark.measure {1000.times {answer_hash = {}; tmp = s.split('_'); answer_hash[tmp.shift[3..-1].to_i] = tmp.shift while tmp[0] } }
# ### Long string using string.split (shorter benchmark)
# => 0.690000 0.000000 0.690000 ( 0.693698)
puts Benchmark.measure {1000.times {ids, answers = *s.scan(/(?<=ID:)(\d+)_([^_]+)/).transpose } }
# ### Long string using string.scan Regexp (shorter benchmark)
# => 0.900000 0.000000 0.900000 ( 0.901358)
大声笑...正则表达式本质上并不慢。当你做很多回溯时,它们很慢。在这里,不会有任何回溯,因为'ID:'和'_'在* id *和* answers *开始和结束的位置设置了明确的界限。正如您在基准测试中看到的那样,输入数据量更大,正则表达式和非正则表达式解决方案之间没有任何渐进性差异。正则表达式解决方案中的常量操作需要更多的准备时间。你不应该让微优化运行你的代码。 OP还表示他正在处理*“长串”*。 – ndn
@ndn - 很高兴知道:-) ...我想你是对的。我通常只在代码要在内部循环内反复运行(例如,它是常用的解析引擎的一部分)时才让微代码运行我的代码。如果代码不经常重复,我通常会选择更清晰的代码。机器越来越强大,“更快的代码”并不总是意味着“更好的代码”了。 – Myst
另外我更新了我的正则表达式,因为它并不真的需要'ID:'部分的后视,并且删除了手动解包。我相信你机器上短字符串的基准测试应该会在** 0.470000 **左右出现。 – ndn
没有正则表达式,我的主张:
i = 0
names = []
ids = []
s = "ID:1_Answer1_ID:2_Answer2_ID:3_AnswerRandom_ID:789_Answer3.5"
s.split("_").each do |f|
if i.odd?
names.push(f)
else
ids.push(f.split(":")[1])
end
i+=1
end
有很多这样做的方法。这是一个使用两个连续的split
的,并没有正则表达式。我假设字符串开始ID:
,因为如果不一定是这种情况,则需要进一步说明问题。
ids, answers = s[3..-1].split(/_ID:/).map { |str| str.split('_') }.transpose
#=> [["1", "2", "3", "789"],
# ["Answer1", "Answer2", "AnswerRandom", "Answer3.5"]]
步骤:
t = s[3..-1]
#=> "1_Answer1_ID:2_Answer2_ID:3_AnswerRandom_ID:789_Answer3.5"
a = t.split('_ID:')
#=> ["1_Answer1", "2_Answer2", "3_AnswerRandom", "789_Answer3.5"]
b = a.map { |str| str.split('_') }
#=> [["1", "Answer1"], ["2", "Answer2"],
# ["3", "AnswerRandom"], ["789", "Answer3.5"]]
b.transpose
#=> [["1", "2", "3", "789"],
# ["Answer1", "Answer2", "AnswerRandom", "Answer3.5"]]
很好的解决方案,并很好的解释。 –
非常干净的解决方案。非常感谢! – rak
但它不会捕获空白答案值。例如,问题1没有任何答案。 s =“ID:1__ID:2_Answer2_ID:3_AnswerRandom_ID:789_Answer3.5” – rak