Skip to content

Commit dc7432a

Browse files
committed
Added new codec: bcd
1 parent a4ce5b0 commit dc7432a

4 files changed

Lines changed: 115 additions & 1 deletion

File tree

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ This library extends the native `codecs` library and provides some new encodings
2020
`barbie-N` | text <-> barbie ciphertext | aka Barbie Typewriter (N belongs to [1, 4])
2121
`baseXX` | text <-> baseXX | see [base encodings](https://python-codext.readthedocs.io/en/latest/base.html)
2222
`baudot` | text <-> Baudot code bits | supports CCITT-1, CCITT-2, EU/FR, ITA1, ITA2, MTK-2 (Python3 only), UK, ...
23+
`bcd` | text <-> binary coded decimal text | encodes characters from their (zero-left-padded) ordinals
2324
`braille` | text <-> braille symbols | Python 3 only
2425
`dna` | text <-> DNA-N sequence | implements the 8 rules of DNA sequences (N belongs to [1,8])
2526
`excess3` | text <-> XS3 encoded text | uses Excess-3 (aka Stibitz code) binary encoding to convert characters from their ordinals
@@ -50,6 +51,8 @@ A few variants are also implemented.
5051
:---: | :---: | ---
5152
`baudot-spaced` | text <-> Baudot code groups of bits | groups of 5 bits are whitespace-separated
5253
`baudot-tape` | text <-> Baudot code tape | outputs a string that looks like a perforated tape
54+
`bcd-extended0` | text <-> BCD-extended text | encodes characters from their (zero-left-padded) ordinals using prefix bits `0000`
55+
`bcd-extended1` | text <-> BCD-extended text | encodes characters from their (zero-left-padded) ordinals using prefix bits `1111`
5356
`manchester-inverted` | text <-> manchester encoded text | XORes each bit of the input with `10`
5457
`octal-spaced` | text <-> octal digits (whitespace-separated) | dummy octal conversion
5558
`ordinal-spaced` | text <-> ordinal digits (whitespace-separated) | dummy character ordinals conversion

codext/binary/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# -*- coding: UTF-8 -*-
22
from .baudot import *
3+
from .bcd import *
34
from .excess3 import *
45
from .gray import *
56
from .manchester import *

codext/binary/bcd.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# -*- coding: UTF-8 -*-
2+
"""BCD Codec - Binary Coded Decimal content encoding.
3+
4+
This codec:
5+
- en/decodes strings from str to str
6+
- en/decodes strings from bytes to bytes
7+
- decodes file content to str (read)
8+
- encodes file content from str to bytes (write)
9+
"""
10+
from ..__common__ import *
11+
12+
13+
__examples1__ = {
14+
'enc(bcd|binary-coded-decimal|binary_coded_decimal)': {
15+
'This is a test!': "\x08A\x04\x10Q\x15\x03!\x05\x11P2\tp2\x11a\x01\x11Q\x16\x030",
16+
},
17+
'dec(binary-coded-decimal)': {
18+
'\xaf': None,
19+
'\xff': None,
20+
'\x08A\x04\x10Q\x15\x03!\x05\x11P2\tp2\x11a\x01\x11Q\x16\x030': "This is a test!",
21+
},
22+
}
23+
__examples2__ = {
24+
'enc(bcd-ext0|bcd_extended_zeros)': {
25+
'This is a test': "\x00\x08\x04\x01\x00\x04\x01\x00\x05\x01\x01\x05\x00\x03\x02\x01\x00\x05\x01\x01\x05\x00"
26+
"\x03\x02\x00\t\x07\x00\x03\x02\x01\x01\x06\x01\x00\x01\x01\x01\x05\x01\x01\x06\x00",
27+
},
28+
}
29+
__examples3__ = {
30+
'enc(bcd-ext1|bcd_extended_ones)': {
31+
'This is a test': "\xf0\xf8\xf4\xf1\xf0\xf4\xf1\xf0\xf5\xf1\xf1\xf5\xf0\xf3\xf2\xf1\xf0\xf5\xf1\xf1\xf5\xf0"
32+
"\xf3\xf2\xf0\xf9\xf7\xf0\xf3\xf2\xf1\xf1\xf6\xf1\xf0\xf1\xf1\xf1\xf5\xf1\xf1\xf6\xf0",
33+
},
34+
}
35+
36+
37+
CODE = {str(i): bin(i)[2:].zfill(4) for i in range(10)}
38+
39+
40+
class BCDDecodeError(ValueError):
41+
pass
42+
43+
44+
def bcd_encode(prefix=""):
45+
def encode(text, errors="strict"):
46+
r, bits = "", prefix
47+
for c in text:
48+
for i in str(ord(c)).zfill(3):
49+
bits += CODE[i]
50+
if len(bits) == 8:
51+
r += chr(int(bits, 2))
52+
bits = prefix
53+
if len(bits) > 0:
54+
r += chr(int(bits + "0000", 2))
55+
return r, len(b(text))
56+
return encode
57+
58+
59+
def bcd_decode(prefix=""):
60+
def decode(text, errors="strict"):
61+
code = {v: k for k, v in CODE.items()}
62+
r, d = "", ""
63+
for i, c in enumerate(text):
64+
bin_c = bin(ord(c))[2:].zfill(8)
65+
for k in range(len(prefix), 8, 4):
66+
hb = bin_c[k:k+4]
67+
try:
68+
d += code[hb]
69+
except KeyError:
70+
d += handle_error("bcd", errors, BCDDecodeError, decode=True)(hb, i)
71+
if len(d) == 3:
72+
r += chr(int(d))
73+
d = ""
74+
return r, len(b(text))
75+
return decode
76+
77+
78+
add("bcd", bcd_encode(), bcd_decode(), pattern=r"^(?:bcd|binary[-_]coded[-_]decimals?)$", examples=__examples1__)
79+
add("bcd-extended0", bcd_encode("0000"), bcd_decode("0000"), examples=__examples2__,
80+
pattern=r"^(?:bcd|binary[-_]coded[-_]decimals?)[-_]ext(?:ended)?(?:[-_]?0|[-_]zeros?)$")
81+
add("bcd-extended1", bcd_encode("1111"), bcd_decode("1111"), examples=__examples3__,
82+
pattern=r"^(?:bcd|binary[-_]coded[-_]decimals?)[-_]ext(?:ended)?(?:[-_]?1|[-_]ones?)$")
83+

docs/binary.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,36 @@ It supports various formats such as CCITT-1 and CCITT-2, ITA1 and ITA2, and some
6060

6161
-----
6262

63+
### Binary Coded Decimal (BCD)
64+
65+
It converts characters to their odrinals, left-pads with zeros, converts digits to 4-bits groups and then make characters with the assembled groups. It can also use a 4-bits prefix for making new characters. It then allows to define extended versions of BCD.
66+
67+
**Codec** | **Conversions** | **Aliases** | **Comment**
68+
:---: | :---: | --- | ---
69+
`bcd` | text <-> BCD encoded text | `binary_coded_decimals` |
70+
`bcd-extended0` | text <-> BCD encoded text using prefix `0000` | `bcd_ext0`, `bcd-extended-zeros`, `binary_coded_decimals_extended_0` |
71+
`bcd-extended1` | text <-> BCD encoded text using prefix `1111` | `bcd_ext1`, `bcd-extended-ones`, `binary_coded_decimals_extended_1` |
72+
73+
```python
74+
>>> codext.encode("Test", "bcd")
75+
'\x08A\x01\x11Q\x16'
76+
>>> codext.decode("\x08A\x01\x11Q\x16", "binary_coded_decimal")
77+
'Test'
78+
>>> codext.encode("Test", "bcd_ext_zero")
79+
'\x00\x08\x04\x01\x00\x01\x01\x01\x05\x01\x01\x06\x00'
80+
>>> codext.decode("\x00\x08\x04\x01\x00\x01\x01\x01\x05\x01\x01\x06\x00", "bcd-ext0")
81+
'Test'
82+
>>> codext.encode("Test", "bcd_extended_ones")
83+
'\xf0\xf8\xf4\xf1\xf0\xf1\xf1\xf1\xf5\xf1\xf1\xf6\xf0'
84+
>>> codext.decode("\xf0\xf8\xf4\xf1\xf0\xf1\xf1\xf1\xf5\xf1\xf1\xf6\xf0", "bcd_ext1")
85+
'Test'
86+
```
87+
88+
-----
89+
6390
### Excess-3
6491

65-
Also called *Stibitz code*, it converts letters to ordinals, left-pads with zeros and then applies Excess-3 (Stibitz) code to get groups of 4 bits that are finally reassembled into bytes.
92+
Also called *Stibitz code*, it converts characters to ordinals, left-pads with zeros and then applies Excess-3 (Stibitz) code to get groups of 4 bits that are finally reassembled into bytes.
6693

6794
**Codec** | **Conversions** | **Aliases** | **Comment**
6895
:---: | :---: | --- | ---

0 commit comments

Comments
 (0)