Ruby ruby quiz

hooopo · 2021年01月09日 · 最后由 quakewang 回复于 2021年01月11日 · 1150 次阅读

需求是这样的,有一段字符串,是把一个 hash 经过简单编码产生的:

  • 规则 1:key 是固定的,比如:id,nm,qt,pr 等等
  • 规则 2:value 里如果含有~会被替换成~~
  • 规则 3:最终结果使用~连接起来

代码形式;

{id: 'P111', nm: '~nm xxx ~~~~ ~id~br~', qt: 10}.map{
  |k,v| [k, v.to_s.gsub("~", "~~")].join 
}.join("~")
# idP111~nm~~nm xxx ~~~~~~~~ ~~id~~br~~~qt10

问题是如何把idP111~nm~~nm xxx ~~~~~~~~ ~~id~~br~~~qt10 decode 成 {id: 'P111', nm: '~nm xxx ~~~~ ~id~br~', qt: 10}

俺试试勒

s = "idP111~nm~~nm xxx ~~~~~~~~ ~~id~~br~~~qt10"
KEY = %w(id nm qt pr)
groups = s.chars.chunk_while do |before, after|
  (before == '~' && after == '~') or (before != '~' && after != '~')
end.map(&:join)
p groups #=》["idP111", "~", "nm", "~~", "nm xxx ", "~~~~~~~~", " ", "~~", "id", "~~", "br", "~~~", "qt10"]

kv = groups.slice_after { |e| e[0] == '~' and e.size.odd? }.map do |arr|
  arr.map do |str|
    if str[0] == '~'
      '~' * (str.size/2)
    else
      str
    end
  end.join
end
p kv #=》["idP111", "nm~nm xxx ~~~~ ~id~br~", "qt10"]

rst = kv.each_with_object({}) do |e, obj|
  if key = KEY.find { |k| key = e.match?(/^(#{k})/) }
    obj[key.to_sym] = e[key.size..-1]
  else
    raise 'Wrong code'
  end
end
p rst #=》{:id=>"P111", :nm=>"~nm xxx ~~~~ ~id~br~", :qt=>"10"}
# rebuild-hash.rb
str = 'idP111~nm~~nm xxx ~~~~~~~~ ~~id~~br~~~qt10'

KEYS = %w[id nm qt pr]

# 'idP111' => ['id', 'P111']
def unjoin(pair)
  key = KEYS.detect{ |key| pair[/^#{key}/] }
  value = pair[/(?<=#{key}).*/].gsub('^^', '~')
  [key, value]
end

puts str.gsub(/~{2}/, '^^').split('~').map{ |pair| unjoin(pair) }.to_h
# => {"id"=>"P111", "nm"=>"~nm xxx ~~~~ ~id~br~", "qt"=>"10"}

decode 的难点在 N 个~,把 ~~ 替换成 ^^ ,让正则去做复杂的事情

这个处理没考虑 corner case,比如有两个 key: nmnmok 的场景,pair[/^#{key}/] 就不准确了

补充一下:

~~ 替换成 ^^ 可行是因为规则 2 使得 encode 后 value 里的 ~ 必然是双数,规则 3 的连接 ~ 必然是 N 个双数 ~ 的最后一个,除非 id 前会有 ~

^^ 可以换成任何小概率出现在 value 的字符串,比如 ^^^^^^^^^

zhengpd 回复

补充一下:

~~ 替换成 ^^ 可行是因为规则 2 使得 encode 后 value 里的 ~ 必然是双数,规则 3 的连接 ~ 必然是 N 个双数 ~ 的最后一个,除非 id 前会有 ~

^^ 可以换成任何小概率出现在 value 的字符串,比如 ^^^^^^^^^

4 楼 已删除

四楼发了什么 被我误删了……

zhengpd 回复

正则方案

require 'strscan'

str = 'idP111~nm~~nm xxx ~~~~~~~~ ~~id~~br~~~qt10'

scanner = StringScanner.new(str)

KEYS = %w[id nm qt br]
KEYS_REG = KEYS.join("|")

result = {}

KEYS.size.times do 
  segment = scanner.scan(/(#{KEYS_REG})([^~]|(?:~~))*(?:$|~(?=#{KEYS_REG}))/)
  next unless segment
  matched = segment.match(/^(#{KEYS_REG})(.*?)~?$/)
  next if matched[1].nil? || matched[2].nil?
  result[matched[1]] = matched[2].gsub("~~", "~")
end
p result
hooopo 回复

我恢复了,后台增加了了撤销删除功能。

hooopo 回复

受 scan 启发,调整了一下,似乎也可以

str = 'idP111~nm~~nm xxx ~~~~~~~~ ~~id~~br~~~qt10'
keys = %w[id nm qt pr]

pairs = str.scan(/(#{keys.join('|')})(.+?[^~](?:~~)*(?=(?:~[^~])|$))/)
pairs.each_with_object({}) { |(k, v), hash| hash[k] = v.gsub('~~', '~') }

one line style

'idP111~nm~~nm xxx ~~~~~~~~ ~~id~~br~~~qt10'.scan(/(id|nm|qt|pr)(.+?[^~](?:~~)*(?=(?:~[^~])|$))/).to_h.transform_values{|s| s.gsub('~~', '~')}
需要 登录 后方可回复, 如果你还没有账号请 注册新账号