~/problems / Streams & durability / Serialization and durability

Escaped key=value settings file

easy ~15 min

A plant nursery's label printer stores its settings as a plain text file, one key=value per line. That works until a value contains a newline (a two-line label) or a key contains =. The Basics drill fixed this with length prefixes. Here you fix it the other classic way, by escaping, so the file stays readable text.

Implement:

  • dumps(settings: dict[str, str]) -> str
  • loads(text: str) -> dict[str, str]

with loads(dumps(d)) == d for every dict of strings, keeping insertion order. Use exactly this format:

  • One line per entry, in the dict's order: escape(key) + "=" + escape(value) + "\n". Every line, including the last, ends with a real newline. An empty dict gives "".
  • escape replaces \ with \\, = with \=, and a newline with \n (backslash followed by the letter n). Nothing else changes.
dumps({"size": "small", "a=b": "line1\nline2", "dir": "C:\\labels"})
# 'size=small\na\\=b=line1\\nline2\ndir=C:\\\\labels\n'
# as the file shows it:
#   size=small
#   a\=b=line1\nline2
#   dir=C:\\labels

loads("x=\n=y\n")   # {"x": "", "": "y"}
loads("")           # {}

loads must raise ValueError for a malformed file:

  • non-empty text that doesn't end in a newline;
  • a line with no unescaped =, or with more than one;
  • a \ followed by anything other than \, = or n, or at the very end of a line;
  • the same key twice.
Show hint

In loads, split on real newlines, then scan each line character by character: on \ look at the next character and emit the character it stands for; an unescaped = switches from building the key to building the value.

Topic: Serialization and durability. Length-prefixed encodings, write-ahead logs, checkpoints and recovery.

0:00
Ctrl ' run · Ctrl ↵ submit
esc