Skip to content

Commit f48fcac

Browse files
committed
Added new codec: a1z26
1 parent 68311d2 commit f48fcac

5 files changed

Lines changed: 83 additions & 2 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ o
165165

166166
**Codec** | **Conversions** | **Comment**
167167
:---: | :---: | ---
168+
`a1z26` | text <-> alphabet order numbers | keeps words whitespace-separated and uses a custom character separator
168169
`affine` | text <-> affine ciphertext | aka Affine Cipher
169170
`ascii85` | text <-> ascii85 encoded text | Python 3 only
170171
`atbash` | text <-> Atbash ciphertext | aka Atbash Cipher

codext/common/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# -*- coding: UTF-8 -*-
2+
from .a1z26 import *
23
from .dummy import *
34
from .octal import *
45
from .ordinal import *

codext/common/a1z26.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# -*- coding: UTF-8 -*-
2+
"""A1Z26 Codec - A1Z26 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 string import ascii_lowercase as lower
11+
12+
from ..__common__ import *
13+
14+
15+
SEP = "-_/|,;:*"
16+
17+
__examples__ = {
18+
'enc(a1z26-BAD)': None,
19+
'dec(a1z26)': {'1-12-123': None},
20+
'enc(a1z26)': {'test123': None, 'this is a test': "20-8-9-19 9-19 1 20-5-19-20"},
21+
'enc(a1z26-/)': {'this is a test': "20/8/9/19 9/19 1 20/5/19/20"},
22+
}
23+
__guess__ = ["a1z26", "a1z26_"] + ["a1z26-" + s for s in SEP[2:]]
24+
25+
26+
def a1z26_encode(sep):
27+
sep = sep[-1] if len(sep) > 0 else "-"
28+
def encode(text, errors="strict"):
29+
words = []
30+
for word in text.split():
31+
w = []
32+
for k, c in enumerate(word):
33+
try:
34+
w.append(str(lower.index(c.lower()) + 1))
35+
except ValueError:
36+
w.append(handle_error("a1z26", errors)(c, k))
37+
words.append(sep.join(w).strip(sep))
38+
return " ".join(words), len(text)
39+
return encode
40+
41+
42+
def a1z26_decode(sep):
43+
sep = sep[-1] if len(sep) > 0 else "-"
44+
def decode(text, errors="strict"):
45+
k, words = 0, []
46+
for word in text.split():
47+
w = ""
48+
for i in word.split(sep):
49+
k += 1
50+
try:
51+
w += lower[int(i)-1]
52+
except IndexError:
53+
w += handle_error("a1z26", errors, decode=True)(str(i), k)
54+
words.append(w)
55+
return " ".join(words), len(text)
56+
return decode
57+
58+
59+
add("a1z26", a1z26_encode, a1z26_decode, pattern=r"^a1z26(|[-_]|[-_][/|,;:\*])$")
60+

docs/enc/common.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,23 @@
22

33
-----
44

5+
### A1Z26
6+
7+
This simple codec converts letters to their order number in the alphabet using a separator between characters and keeping words separated by a whitespace. It is similar to the [`consonant-vowel-indices`](others.html#letter-indices) encoding.
8+
9+
**Codec** | **Conversions** | **Aliases** | **Comment**
10+
:---: | :---: | --- | ---
11+
`a1z26` | text <-> alphabet order numbers | `a1z26`, `a1z26-/`, `a1z26-,`, ... | this codec does not preserve the case and is dynamic (separator of characters in each word can be customized among these: "`-_/|,;:*`")
12+
13+
```python
14+
>>> codext.encode("This is a test", "a1z26")
15+
'20-8-9-19 9-19 1 20-5-19-20'
16+
>>> codext.decode("20-8-9-19 9-19 1 20-5-19-20", "a1z26")
17+
'this is a test'
18+
```
19+
20+
-----
21+
522
### Octal
623

724
This simple codec converts characters into their octal values.

docs/features.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ In the second example, we can see that the given encoded string is not decoded a
302302
('This is a test', ('base62', 'base64'))
303303
```
304304

305-
Instead of a string, we can also pass a function. For this purpose, standard stop functions are predefined in the `stopfunc` submodule. So, we can for instance use `stopfunc.lang_en` to stop when we find something that is English (only works if [`langdetect`](https://pypi.org/project/langdetect/) is installed). Note that working this way gives lots of false positives if the text is very short like in the example case. That's why the `codec_categories` argument is used to only consider baseX codecs. This is also demonstrated in the next examples.
305+
Instead of a string, we can also pass a function. For this purpose, standard stop functions are predefined in the `stopfunc` submodule. So, we can for instance use `stopfunc.lang_en` to stop when we find something that is English (only works if [`langdetect`](https://pypi.org/project/langdetect/) is installed, which is willingly NOT in the requirements of this package). Note that working this way gives lots of false positives if the text is very short like in the example case. That's why the `codec_categories` argument is used to only consider baseX codecs. This is also demonstrated in the next examples.
306306

307307
```python
308308
>>> codext.guess("CJG3Ix8bVcSRMLOqwDUg28aDsT7", codext.stopfunc.lang_en, codec_categories="base")
@@ -332,6 +332,8 @@ Another example of 2-stages encoded string:
332332
('this is a test', ('base64', 'morse'))
333333
```
334334

335+
When multiple results are expected, `stop` and `show` arguments can be used respectively to avoid stopping while finding a result and to display the intermediate result.
336+
335337
!!! warning "Computation time"
336338

337339
Note that, in the very last examples, the first call takes much longer than the second one but requires no knowledge about the possible [categories](#list-codecs) of encodings.
@@ -341,7 +343,7 @@ Another example of 2-stages encoded string:
341343
Currently, a few standard stop functions are provided with the `stopfunc` submodule:
342344

343345
- `flag`: searches for the pattern "`[Ff][Ll1][Aa4@][Gg9]`" (either UTF-8 or UTF-16)
344-
- `lang_**`: checks if the given lang (any from the [`PROFILES_DIRECTORY`](https://github.com/Mimino666/langdetect/tree/master/langdetect/profiles) of the [`langdetect` module](https://github.com/Mimino666/langdetect)) is detected (note that it first checks if all characters are printable)
346+
- `lang_**`: checks if the given lang (any from the [`PROFILES_DIRECTORY`](https://github.com/Mimino666/langdetect/tree/master/langdetect/profiles) of the [`langdetect` module](https://github.com/Mimino666/langdetect) if it is installed) is detected (note that it first checks if all characters are printable)
345347
- `printables`: checks that every output character is in the set of printables
346348

347349
-----

0 commit comments

Comments
 (0)