Skip to content

ASCII Table — Python source

A searchable, filterable reference for all 128 ASCII code points. See the decimal, hex, octal, and binary form of every character, narrow to printable characters only, clamp the code-point range, and click any row to copy. Runs 100% client-side.

This is the Python implementation — the same logic the interactive tool runs, in a shareable, citable form.

"""ASCII Table — pure logic (Python port).

CosmoDev polyglot showcase port of the `ascii-table` tool, ported from
``src/lib/ascii-table.ts``. Every output is a pure function of the code point
0–127; no I/O, no third-party packages — the Python standard library only.

Display source — part of CosmoDev's polyglot tool pages.

Each of the 128 ASCII code points is described by its decimal / hex / octal /
binary forms, a display glyph, a human name, whether it is printable, whether
it is a control character, and (for control chars that have one) a C-style
escape sequence.
"""

from __future__ import annotations

from dataclasses import dataclass
from functools import lru_cache
from typing import Optional


@dataclass(frozen=True)
class Entry:
    """A single ASCII code point and its derived representations."""

    dec: int
    hex: str
    oct: str
    binary: str
    char: str
    name: str
    printable: bool
    control: bool
    escape: Optional[str] = None


# Official ASCII control-character names (0–31 and 127).
_CONTROL_NAMES = {
    0: "Null (NUL)",
    1: "Start of Heading (SOH)",
    2: "Start of Text (STX)",
    3: "End of Text (ETX)",
    4: "End of Transmission (EOT)",
    5: "Enquiry (ENQ)",
    6: "Acknowledge (ACK)",
    7: "Bell (BEL)",
    8: "Backspace (BS)",
    9: "Horizontal Tab (HT)",
    10: "Line Feed (LF)",
    11: "Vertical Tab (VT)",
    12: "Form Feed (FF)",
    13: "Carriage Return (CR)",
    14: "Shift Out (SO)",
    15: "Shift In (SI)",
    16: "Data Link Escape (DLE)",
    17: "Device Control 1 (DC1)",
    18: "Device Control 2 (DC2)",
    19: "Device Control 3 (DC3)",
    20: "Device Control 4 (DC4)",
    21: "Negative Acknowledge (NAK)",
    22: "Synchronous Idle (SYN)",
    23: "End of Transmission Block (ETB)",
    24: "Cancel (CAN)",
    25: "End of Medium (EM)",
    26: "Substitute (SUB)",
    27: "Escape (ESC)",
    28: "File Separator (FS)",
    29: "Group Separator (GS)",
    30: "Record Separator (RS)",
    31: "Unit Separator (US)",
    127: "Delete (DEL)",
}

# C-style escape sequences for the control chars that have a standard/common
# one. Each value is the literal 2-character source token (backslash + letter).
_ESCAPES = {
    0: "\\0",
    7: "\\a",
    8: "\\b",
    9: "\\t",
    10: "\\n",
    11: "\\v",
    12: "\\f",
    13: "\\r",
    27: "\\e",
}

# Names for printable punctuation & symbols; letter and digit names are derived.
_SYMBOL_NAMES = {
    32: "Space",
    33: "Exclamation mark",
    34: "Quotation mark",
    35: "Number sign",
    36: "Dollar sign",
    37: "Percent sign",
    38: "Ampersand",
    39: "Apostrophe",
    40: "Left parenthesis",
    41: "Right parenthesis",
    42: "Asterisk",
    43: "Plus sign",
    44: "Comma",
    45: "Hyphen / Minus",
    46: "Full stop",
    47: "Slash",
    58: "Colon",
    59: "Semicolon",
    60: "Less-than sign",
    61: "Equals sign",
    62: "Greater-than sign",
    63: "Question mark",
    64: "At sign",
    91: "Left bracket",
    92: "Backslash",
    93: "Right bracket",
    94: "Circumflex / Caret",
    95: "Underscore",
    96: "Grave accent",
    123: "Left brace",
    124: "Vertical bar",
    125: "Right brace",
    126: "Tilde",
}


def _letter_name(code: int) -> str:
    """Derive a name for a printable letter or digit."""
    if 48 <= code <= 57:  # '0'..'9'
        return f"Digit {code - 48}"
    if 65 <= code <= 90:  # 'A'..'Z'
        return f"Uppercase {chr(code)}"
    if 97 <= code <= 122:  # 'a'..'z'
        return f"Lowercase {chr(code)}"
    return chr(code)


def _make_entry(code: int) -> Entry:
    """Build a single entry from its code point (0–127)."""
    control = code <= 31 or code == 127

    # Control-picture glyph: U+2400 for 0–31, U+2421 ("symbol for delete") for DEL.
    if control:
        char = chr(0x2421 if code == 127 else 0x2400 + code)
    else:
        char = chr(code)

    if control:
        name = _CONTROL_NAMES[code]
    else:
        name = _SYMBOL_NAMES.get(code) or _letter_name(code)

    escape = _ESCAPES.get(code) if control else None

    return Entry(
        dec=code,
        hex=f"0x{code:02X}",
        oct=f"{code:03o}",
        binary=f"{code:08b}",
        char=char,
        name=name,
        printable=not control,
        control=control,
        escape=escape,
    )


@lru_cache(maxsize=1)
def entries() -> tuple[Entry, ...]:
    """The full 128-entry ASCII table, indexed by code point.

    Built once and cached for the process lifetime (the table is immutable, so
    a shared tuple is safe for all callers).
    """
    return tuple(_make_entry(i) for i in range(128))


def get_entry(code: int) -> Optional[Entry]:
    """Look up a single entry by code point.

    Returns ``None`` for out-of-range or non-integer input. Booleans are
    rejected explicitly: in Python ``bool`` subclasses ``int``, so ``True`` and
    ``False`` would otherwise leak through as ``1`` and ``0``. TypeScript's
    ``Number.isInteger`` treats booleans as non-numbers, and we mirror that.
    """
    if isinstance(code, bool) or not isinstance(code, int):
        return None
    if code < 0 or code > 127:
        return None
    return entries()[code]


def filter_table(
    query: str = "",
    printable_only: bool = False,
    min: int = 0,  # noqa: A002 - mirrors the tool's public parameter names
    max: int = 127,  # noqa: A002 - mirrors the tool's public parameter names
) -> list[Entry]:
    """Filter the table.

    ``query`` is a case-insensitive substring matched against dec / hex / oct /
    binary / name / char / escape. ``printable_only`` hides controls.
    ``min`` / ``max`` clamp the decimal range (defaults 0–127).
    """
    q = (query or "").strip().lower()
    out: list[Entry] = []
    for e in entries():
        if printable_only and not e.printable:
            continue
        if e.dec < min or e.dec > max:
            continue
        if not q:
            out.append(e)
            continue
        haystack = (
            str(e.dec),
            e.hex.lower(),
            e.oct,
            e.binary,
            e.name.lower(),
            e.char.lower(),
            e.escape or "",
        )
        if any(q in h for h in haystack):
            out.append(e)
    return out

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →