Phrogz said:
Here's what I consider a slightly cleaner, more robust version:
require 'strscan'
inp = "3:ab23:cat5:sheep"
s = StringScanner.new( inp )
words = []
until s.eos?
begin
unless digits = s.scan( /\d+(?=
/ )
raise "I can't find an integer followed by a colon"
end
s.pos += 1 # skip the colon we know is there
digits = digits.to_i
unless s.rest_size >= digits
raise "I ran out of characters; looking for #{digits} characters,
#{s.rest_size} left"
end
words << s.peek( digits )
s.pos += digits
rescue RuntimeError => e
warn "Looking at #{s.rest.inspect},"
warn e.message
abort "Words found so far: #{words.inspect}"
end
end
puts "Words found: ", words
Thanks for the help!
I ended up doing it slightly different (after a lot of pain, and a sneek
peek at a Python script.):
This is an early library that handles bencoding. Note that the error
handling is pretty much non-existent at this point, and it'll break if
you fart on it.
class String
def bencode
"#{length}:#{self}"
end
end
class Numeric
def bencode
"i#{floor}e"
end
end
class Array
def bencode
"l#{map{|obj| obj.bencode}}e"
end
end
class Hash
def bencode
"d#{map{|key, val| [key.bencode, val.bencode]}}e"
end
end
module Bencode
@filter = {:integer => /(0|-?[1-9]+\d*)e/,
:string => /(0|[1-9][0-9]*):/}
class << self
attr_reader :map
def dump(obj)
obj.bencode
end
def load(str)
decode(str, 0).first
end
def decode_integer(data, i)
match = @filter[:integer].match(data[i..-1])
raise ArgumentError if match.nil?
return Integer(match[1]), match.end(0) + i
end
def decode_string(data, i)
match = @filter[:string].match(data[i..-1])
raise ArgumentError if match.nil?
length = Integer(match[1])
offset = match.end(0) + length + i
return data[(match.end(0) + i)...offset], offset
end
def decode_list(data, i)
ary = []
while data[i..i] != "e"
val, i = decode(data, i)
ary << val
end
return ary, i + 1
end
def decode_dict(data, i)
hsh = {}
while data[i..i] != "e"
key, i = decode_string(data, i)
val, i = decode(data, i)
hsh[key] = val
end
return hsh, i + 1
end
def decode(data, i)
case data[i..i]
when "i"
decode_integer(data, i + 1)
when "l"
decode_list(data, i + 1)
when "d"
decode_dict(data, i + 1)
else
decode_string(data, i)
end
end
end
end
Eventually I'll upload it to RubyForge. If you've got comments, bring
'em on, but remember that I only just started this today.
Cheers,
Daniel