Implementation of simple marshaling of nested data structures. Supports: int, float, string, nested lists. Serialized data is presented as string. It's not difficult to serialize into binary...
# POSTPACK -- packing the data in postfix notation (nested typed items)
#
# Format: item '!' type,
# where type may be 'i' (int), 'f' (float), 's' (string), 'l' (list),
# num 'l' (list with num items)
#
# Example: 1!i2!i!l3!i4!i!2l => [[1, 2], [3, 4]]
#
# To escape '!' '!' is used: 'Hello!!' => 'Hello!'
from __future__ import print_function
import string
DIGITS = string.digits
class Postpack:
def __init__(self, fmt={}):
self.fmt = {int: "%d", float: "%f", str: "%s"}
self.fmt.update(fmt)
def decode(self, input):
''' Decodes input string into internal representation (list of items,
possible nested):
>>> Postpack().decode("Hello, world!!!!!s2.1!f!2l3!i4!i!2l5!i!2l")
[['Hello, world!!', 2.1], [[3, 4], 5]]
'''
buf = "" # item chars buffer
instr = "" # instruction chars buffer
items = [] # items
st = "data" # FSM state
for ch in input:
if st == "instr":
if ch in DIGITS:
instr += ch
elif ch == '!':
buf += ch
st = "data"
else:
if ch == 'i':
items.append(int(buf))
elif ch == 'f':
items.append(float(buf))
elif ch == 's':
items.append(str(buf))
elif ch == 'l':
nitems = len(items)
count = int(instr) if instr else nitems
first = nitems - count
if first < 0:
raise ValueError("Wrong count: %d" % count)
items[first:] = [items[first:]]
else:
raise ValueError("Wrong instruction: %s" % ch)
st = "data"
buf = ""
elif st == "data":
if ch == "!":
instr = ""
st = "instr"
else:
buf += ch
return items
@staticmethod
def escape(s):
'''
>>> Postpack().escape('Qwe!rty!!')
'Qwe!!rty!!!!'
'''
return s.replace('!', '!!')
def encode(self, items):
''' Encodes list of items (may be nested):
>>> Postpack(fmt={float: '%.1f'}).encode([['Hello, world!!', 2.1], [[3, 4], 5]])
'Hello, world!!!!!s2.1!f!2l3!i4!i!2l5!i!2l'
'''
output = ""
for it in items:
ittype = type(it)
if ittype is int:
output += self.fmt[int] % it + "!i"
elif ittype is float:
output += self.fmt[float] % it + "!f"
elif ittype is str:
output += self.fmt[str] % self.escape(it) + "!s"
elif ittype is list:
output += self.encode(it) + "!%dl" % len(it)
else:
raise ValueError("Wrong type: %s" % ittype)
return output
##############################################################################
if __name__ == "__main__":
import doctest
doctest.testmod()
Комментариев нет:
Отправить комментарий
Thanks for your posting!