Skip to content

ASCII Table — C# 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 C# implementation — the same logic the interactive tool runs, in a shareable, citable form.

// ASCII Table — pure logic (C# port): the 128-entry ASCII reference table with
// search and range filters.
//
// Language: C# 12 (.NET 8) — standard library only (System, System.Collections.Generic, System.Linq).
// Source: CosmoDev polyglot showcase port; canonical = src/lib/ascii-table.ts
//         + this tool's python.py / rust.rs.
// License: 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.

#nullable enable

namespace CosmoDev.AsciiTable;

/// <summary>A single ASCII code point and its derived representations.</summary>
/// <param name="Dec">Decimal code point, 0–127.</param>
/// <param name="Hex">Uppercase hex, e.g. "0x41".</param>
/// <param name="Oct">Zero-padded 3-digit octal, e.g. "101".</param>
/// <param name="Binary">Zero-padded 8-bit binary, e.g. "01000001".</param>
/// <param name="Glyph">Display glyph: the literal char for printables, a control-picture
/// glyph otherwise. (Named <c>char</c> in the canonical sources; that is a keyword here.)</param>
/// <param name="Name">Human-readable name, e.g. "Line Feed (LF)", "Uppercase A", "Space".</param>
/// <param name="Printable">True for 32–126 (visible or space).</param>
/// <param name="Control">True for 0–31 and 127.</param>
/// <param name="Escape">C-style escape for control chars that have one (e.g. "\n"); null otherwise.</param>
public sealed record Entry(
    int Dec,
    string Hex,
    string Oct,
    string Binary,
    string Glyph,
    string Name,
    bool Printable,
    bool Control,
    string? Escape = null);

/// <summary>The <c>ascii-table</c> tool's pure logic.</summary>
public static class AsciiTable
{
    /// <summary>
    /// Official ASCII control-character names for 0–31 and 127. Expressed as a
    /// switch expression over every control code point; the discard arm is a
    /// defensive guard that documents the precondition (the Rust port uses
    /// unreachable!() for the same case).
    /// </summary>
    private static string ControlName(int code) => code switch
    {
        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)",
        _ => "", // only called with control codes; defensive guard for misuse
    };

    /// <summary>
    /// C-style escape for the control chars that own a standard/common one.
    /// Returns the literal 2-character source token (backslash + letter), or null.
    /// </summary>
    private static string? ControlEscape(int code) => code switch
    {
        0 => "\\0",
        7 => "\\a",
        8 => "\\b",
        9 => "\\t",
        10 => "\\n",
        11 => "\\v",
        12 => "\\f",
        13 => "\\r",
        27 => "\\e",
        _ => null,
    };

    /// <summary>
    /// Name for a printable punctuation / symbol glyph, or null.
    /// Letters and digits are derived in <see cref="LetterName"/>.
    /// </summary>
    private static string? SymbolName(int code) => code switch
    {
        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",
        _ => null,
    };

    /// <summary>Derive a name for a printable letter or digit.</summary>
    private static string LetterName(int code) => code switch
    {
        >= 48 and <= 57 => $"Digit {code - 48}",        // '0'..'9'
        >= 65 and <= 90 => $"Uppercase {(char)code}",   // 'A'..'Z'
        >= 97 and <= 122 => $"Lowercase {(char)code}",  // 'a'..'z'
        _ => ((char)code).ToString(),
    };

    /// <summary>Build a single entry from its code point (0–127).</summary>
    private static Entry MakeEntry(int code)
    {
        bool control = code <= 31 || code == 127;

        // Control-picture glyph: U+2400 for 0–31, U+2421 ("symbol for delete") for DEL.
        string glyph = control
            ? char.ConvertFromUtf32(code == 127 ? 0x2421 : 0x2400 + code)
            : ((char)code).ToString();

        string name = control ? ControlName(code) : SymbolName(code) ?? LetterName(code);

        return new Entry(
            Dec: code,
            Hex: $"0x{code:X2}",
            Oct: Convert.ToString(code, 8).PadLeft(3, '0'),
            Binary: Convert.ToString(code, 2).PadLeft(8, '0'),
            Glyph: glyph,
            Name: name,
            Printable: !control,
            Control: control,
            Escape: control ? ControlEscape(code) : null);
    }

    // The 128-entry table is built once in the type initializer; .NET guarantees
    // type initializers run exactly once, thread-safely — the CLR equivalent of
    // Rust's OnceLock.
    private static readonly Entry[] _entries = BuildTable();

    private static Entry[] BuildTable()
    {
        var table = new Entry[128];
        for (int i = 0; i < 128; i++)
            table[i] = MakeEntry(i);
        return table;
    }

    /// <summary>The full 128-entry ASCII table, indexed by code point.</summary>
    public static IReadOnlyList<Entry> Entries => _entries;

    /// <summary>
    /// Look up a single entry by code point. Returns null for out-of-range
    /// input (the TypeScript variant returns null; C#'s nullable reference
    /// type annotation carries the same contract).
    /// </summary>
    public static Entry? GetEntry(int code) => code >= 0 && code <= 127 ? _entries[code] : null;

    /// <summary>
    /// Filter the table.
    ///
    /// <paramref name="query"/> is a case-insensitive substring matched against
    /// dec / hex / oct / binary / name / char / escape.
    /// <paramref name="printableOnly"/> hides controls.
    /// <paramref name="min"/> / <paramref name="max"/> clamp the decimal range (defaults 0–127).
    /// </summary>
    public static List<Entry> FilterTable(string query = "", bool printableOnly = false,
                                          int min = 0, int max = 127)
    {
        string q = (query ?? "").Trim().ToLowerInvariant();
        List<Entry> result = new();
        foreach (Entry e in _entries)
        {
            if (printableOnly && !e.Printable)
                continue;
            if (e.Dec < min || e.Dec > max)
                continue;
            if (q.Length > 0)
            {
                bool hit = e.Dec.ToString().Contains(q)
                        || e.Hex.ToLowerInvariant().Contains(q)
                        || e.Oct.Contains(q)
                        || e.Binary.Contains(q)
                        || e.Name.ToLowerInvariant().Contains(q)
                        || e.Glyph.ToLowerInvariant().Contains(q)
                        || (e.Escape ?? "").Contains(q);
                if (!hit)
                    continue;
            }
            result.Add(e);
        }
        return result;
    }
}

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 →