ASCII control characters

From Rosetta Code
Revision as of 22:21, 18 June 2024 by Petelomax (talk | contribs) (→‎{{header|Phix}}: use pygments)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
ASCII control characters is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

ASCII is the American Standard Code for Information Interchange. There are 128 ASCII characters of which the first 32 and the last are 'control characters'.

Task

Show how your language might treat control characters en bloc by using an enum, an enum like structure or other approach to associate their names with their ASCII values thereby enabling them to be retrieved or listed by name.

Technically, the 33rd character 'space' is a printable character, not a control character, though you may treat it as the latter for the purposes of this task.

Reference


ALGOL 68

Algol 68 doesn't have ENUMs but it is easy to create constants, this example shows how a facility similar to Go's iota can be implemented.
Note space is a standard Algol 68 transput (I/O) routine, so spc is used for the spacce character.

    # create constants for the ASCII control characters (0-127)        #
    INT  char value := -1;
    # increments and returns the next value for a character            #
    PROC next char = CHAR: REPR ( char value +:= 1 );
    CHAR nul = next char;
    CHAR soh = next char;
    CHAR stx = next char;
    CHAR etx = next char;
    CHAR eot = next char;
    CHAR enq = next char;
    CHAR ack = next char;
    CHAR bel = next char;
    CHAR bs  = next char;
    CHAR ht  = next char;
    CHAR lf  = next char;
    CHAR vt  = next char;
    CHAR ff  = next char;
    CHAR cr  = next char;
    CHAR so  = next char;
    CHAR si  = next char;
    CHAR dle = next char;
    CHAR dc1 = next char;
    CHAR dc2 = next char;
    CHAR dc3 = next char;
    CHAR dc4 = next char;
    CHAR nak = next char;
    CHAR syn = next char;
    CHAR etb = next char;
    CHAR can = next char;
    CHAR em  = next char;
    CHAR sub = next char;
    CHAR esc = next char;
    CHAR fs  = next char;
    CHAR gs  = next char;
    CHAR rs  = next char;
    CHAR us  = next char;
    CHAR spc = " ";     # using spc as space is a standard transput procedure #
    CHAR del = REPR 127;

# e.g.:                                                                       #
# print( ( nul, soh, ht, lf ) );              prints the characters themselve #
# print( ( ABS nul, ABS soh, ABS ht, ABS lf, lf ) ); prints the characters as #
#                      integers:          +0         +1         +9        +10 #

C

enum: char {
nul,
soh,
stx,
etx,
eot,
enq,
ack,
bel,
bs,
ht,
lf,
vt,
ff,
cr,
so,
si,
dle,
dc1,
dc2,
dc3,
dc4,
nak,
syn,
etb,
can,
em,
sub,
esc,
fs,
gs,
rs,
us,
space,
del = 127
};

D

import std.ascii.ControlChar;

FreeBASIC

Enum AsciiControlChar
    NUL = &H00  'Null
    SOH = &H01  'Star of Header
    STX = &H02  'Start of Text
    ETX = &H03  'End of Text
    EOT = &H04  'End of Transmission
    ENQ = &H05  'Enquiry
    ACK = &H06  'Acknowledge
    BEL = &H07  'Bell
    BS = &H08   'BackSpace
    HT = &H09   'Horizontal Tabulation
    LF = &H0A   'Line Feed
    VT = &H0B   'Vertical Tabulation
    FF = &H0C   'Form Feed
    CR = &H0D   'Carriage Return
    SO = &H0E   'Shift Out
    SI = &H0F   'Shift In
    DLE = &H10  'Data Link Escape
    DC1 = &H11  'Device Control 1 (XON)
    DC2 = &H12  'Device Control 2
    DC3 = &H13  'Device Control 3 (XOFF)
    DC4 = &H14  'Device Control 4
    NAK = &H15  'Negative acknowledge
    SYN = &H16  'Synchronous Idle
    ETB = &H17  'End of Transmission Block
    CAN = &H18  'Cancel
    EM = &H19   'End of Medium
    SUB_ = &H1A 'Substitute
    ESC = &H1B  'Escape
    FS = &H1C   'File Separator
    GS = &H1D   'Group Separator
    RS = &H1E   'Record Separator
    US = &H1F   'Unit Separator
    SP = &H20   'Space
    DEL = &H7F  'Delete
End Enum

Print(Hex(AsciiControlChar.CR))
Print(Hex(AsciiControlChar.DEL))
Sleep
Output:
 D
7F

FutureBasic

local fn ASCIIControlInfo( asciiControl as CFStringRef ) as CFStringRef
asciiControl = lcase(asciiControl)
if fn StringIsEqual( asciiControl, @"space"  ) then asciiControl = @"spc"
if fn StringIsEqual( asciiControl, @"delete" ) then asciiControl = @"del"
CFStringRef        result
CFDictionaryRef asciiDict = @{
@"nul": @"0		0		000		NULL",
@"soh": @"1		01		001		Start of heading",
@"soh": @"2		02		002		Start of text",
@"etx": @"3		03		003		End of text",
@"eot": @"4		04		004		End of transmission",
@"enq": @"5		05		005		Enquiry",
@"ack": @"6		06		006		Acknowledge",
@"bel": @"7		07		007		Bell",
@"bs" : @"8		08		010		Backspace",
@"ht" : @"9		09		011		Horizontal tab",
@"lf" : @"10		0A		012		Line feed",
@"vt" : @"11		0B		013		Vertical tab",
@"ff" : @"12		0C		014		New page/form feed",
@"cr" : @"13		0D		015		Carriage return",
@"so" : @"14		0E		016		Shift out",
@"si" : @"15		0F		017		Shift in",
@"dle": @"16		10		020		Data link escape",
@"dc1": @"17		11		021		Device control 1",
@"dc2": @"18		12		022		Device control 2",
@"dc3": @"19		13		023		Device control 3",
@"dc4": @"20		14		024		Device control 4",
@"nak": @"21		15		025		Negative acknowledge",
@"syn": @"22		16		026		Synchronous idle",
@"etb": @"23		17		027		End of transmission block",
@"can": @"24		18		030		Cancel",
@"em" : @"25		19		031		End of medium",
@"sub": @"26		1A		032		Substitute",
@"esc": @"27		1B		033		Escape",
@"fs" : @"28		1C		034		File separator",
@"gs" : @"29		1D		035		Group separator",
@"rs" : @"30		1E		036		Record separator",
@"us" : @"31		1F		037		Unit separator",
@"spc": @"32		20		040		Space",
@"del": @"127		7F		177		Delete"}

if ( asciiDict[asciiControl] )
result = fn StringWithFormat( @"Ctl\t\tDec\t\tHex\t\tOct\t\tDescription\n%@\t\t%@\n", asciiControl, asciiDict[asciiControl] )
else
result = fn StringWithFormat( @"Error: %@ is not an ASCII control\n", asciiControl )
end if
end fn = result

print fn ASCIIControlInfo( @"vt"  )
print fn ASCIIControlInfo( @"CR"  )
print fn ASCIIControlInfo( @"Si"  )
print fn ASCIIControlInfo( @"ESC" )
print fn ASCIIControlInfo( @"xx"  )
print fn ASCIIControlInfo( @"Space"  )
print fn ASCIIControlInfo( @"DELETE" )

HandleEvents
Ctl		Dec		Hex		Oct		Description
nul		0		0		000		NULL

Ctl		Dec		Hex		Oct		Description
vt		11		0B		013		Vertical tab

Ctl		Dec		Hex		Oct		Description
cr		13		0D		015		Carriage return

Ctl		Dec		Hex		Oct		Description
si		15		0F		017		Shift in

Ctl		Dec		Hex		Oct		Description
esc		27		1B		033		Escape

Error: xx is not an ASCII control

Ctl		Dec		Hex		Oct		Description
spc		32		20		040		Space

Ctl		Dec		Hex		Oct		Description
del		127		7F		177		Delete

Go

Go's support for enums is unconventional in that they are basically a bunch of constants with which a type name may be associated.

The pre-declared identifier 'iota' is typically used with enums and represents successive untyped integer constants, starting from zero though (as here) the sequence may be interrupted by assigning a new value.

package main

import "fmt"

type Ctrl int

const (
    nul Ctrl = iota
    soh
    stx
    etx
    eot
    enq
    ack
    bel
    bs
    ht
    lf
    vt
    ff
    cr
    so
    si
    dle
    dc1
    dc2
    dc3
    dc4
    nak
    syn
    etb
    can
    em
    sub
    esc
    fs
    gs
    rs
    us
    space
    del = 127
)

func main() {
    // print some specimen values
    fmt.Println(cr)
    fmt.Println(del)
}
Output:
13
127

jq

def ascii_control_character_names: [
    "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
    "bs",  "ht",  "lf",  "vt",  "ff",  "cr",  "so",  "si", 
    "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
    "can", "em",  "sub", "esc", "fs",  "gs",  "rs",  "us",
    "space", "del"
];

def Ctrl:
  ascii_control_character_names as $a
  | reduce range(0; $a|length) as $i ({}; .[$a[$i]] = $i)
  | .["del"] = 127;

def examples:
  Ctrl as $Ctrl
  | "Ctrl.cr => \($Ctrl.cr)",
    "Ctrl.del => \($Ctrl.del)",
    "Ctrl.space => \($Ctrl.space)";

examples
Output:
Ctrl.cr => 13
Ctrl.del => 127
Ctrl.space => 32

Julia

Note that Julia recognizes many non-ASCII characters with values above 127 as control Char as well.

julia> filter(iscntrl, Char(0):Char(127))
33-element Vector{Char}:
 '\0': ASCII/Unicode U+0000 (category Cc: Other, control)
 '\x01': ASCII/Unicode U+0001 (category Cc: Other, control)
 '\x02': ASCII/Unicode U+0002 (category Cc: Other, control)
 '\x03': ASCII/Unicode U+0003 (category Cc: Other, control)
 '\x04': ASCII/Unicode U+0004 (category Cc: Other, control)
 '\x05': ASCII/Unicode U+0005 (category Cc: Other, control)
 '\x06': ASCII/Unicode U+0006 (category Cc: Other, control)
 '\a': ASCII/Unicode U+0007 (category Cc: Other, control)
 '\b': ASCII/Unicode U+0008 (category Cc: Other, control)
 '\t': ASCII/Unicode U+0009 (category Cc: Other, control)
 '\n': ASCII/Unicode U+000A (category Cc: Other, control)
 '\v': ASCII/Unicode U+000B (category Cc: Other, control)
 '\f': ASCII/Unicode U+000C (category Cc: Other, control)
 '\r': ASCII/Unicode U+000D (category Cc: Other, control)
 '\x0e': ASCII/Unicode U+000E (category Cc: Other, control)
 '\x0f': ASCII/Unicode U+000F (category Cc: Other, control)
 '\x10': ASCII/Unicode U+0010 (category Cc: Other, control)
 '\x11': ASCII/Unicode U+0011 (category Cc: Other, control)
 '\x12': ASCII/Unicode U+0012 (category Cc: Other, control)
 '\x13': ASCII/Unicode U+0013 (category Cc: Other, control)
 '\x14': ASCII/Unicode U+0014 (category Cc: Other, control)
 '\x15': ASCII/Unicode U+0015 (category Cc: Other, control)
 '\x16': ASCII/Unicode U+0016 (category Cc: Other, control)
 '\x17': ASCII/Unicode U+0017 (category Cc: Other, control)
 '\x18': ASCII/Unicode U+0018 (category Cc: Other, control)
 '\x19': ASCII/Unicode U+0019 (category Cc: Other, control)
 '\x1a': ASCII/Unicode U+001A (category Cc: Other, control)
 '\e': ASCII/Unicode U+001B (category Cc: Other, control)
 '\x1c': ASCII/Unicode U+001C (category Cc: Other, control)
 '\x1d': ASCII/Unicode U+001D (category Cc: Other, control)
 '\x1e': ASCII/Unicode U+001E (category Cc: Other, control)
 '\x1f': ASCII/Unicode U+001F (category Cc: Other, control)
 '\x7f': ASCII/Unicode U+007F (category Cc: Other, control)

An enum can be used for the task:

@enum Control begin
    nul = 0
    soh = 1
    stx = 2
    etx = 3
    eot = 4
    enq = 5
    ack = 6
    bel = 7
    bs = 8
    ht = 9
    lf = 10
    vt = 11
    ff = 12
    cr = 13
    so = 14
    si = 15
    dle = 16
    dc1 = 17
    dc2 = 18
    dc3 = 19
    dc4 = 20
    nak = 21
    syn = 22
    etb = 23
    can = 24
    em = 25
    sub = 26
    esc = 27
    fs = 28
    gs = 29
    rs = 30
    us = 31
    del = 127
end
display(nul), display(ht), display(us), display(del)

A named tuple is a another way to reference such control Chars by name:

const CNTRL = (
    nul = 0,
    soh = 1,
    stx = 2,
    etx = 3,
    eot = 4,
    enq = 5,
    ack = 6,
    bel = 7,
    bs = 8,
    ht = 9,
    lf = 10,
    vt = 11,
    ff = 12,
    cr = 13,
    so = 14,
    si = 15,
    dle = 16,
    dc1 = 17,
    dc2 = 18,
    dc3 = 19,
    dc4 = 20,
    nak = 21,
    syn = 22,
    etb = 23,
    can = 24,
    em = 25,
    sub = 26,
    esc = 27,
    fs = 28,
    gs = 29,
    rs = 30,
    us = 31,
    del = 127,
)
@show CNTRL.nul, CNTRL.ht, CNTRL.us, CNTRL.del
Output:
nul::Control = 0
ht::Control = 9
us::Control = 31
del::Control = 127
(CNTRL.nul, CNTRL.ht, CNTRL.us, CNTRL.del) = (0, 9, 31, 127)

Maxima

Those character can be put in a list and can be found their ASCII numbers.

/* List of characters that are not constituents (constituent is a graphic character but not a space character) */
block(makelist(ascii(i),i,0,127),sublist(%%,lambda([x],not constituent(x))));
/* [,"�","�","�","�","�","�","�","�","�","
","�","�","
","�","�","�","�","�","�","�","�","�","�","�","�","�","�","�","�","�","�"," ","�"] */

/* Ascii numbers of characters that are not constituents */
map(cint,%);
/* [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,127] */

Nim

We define an enumeration type.

For each element, Nim allows to specify its integer value and its string representation. Here, the integer value is implicit (starting from zero) except for DEL whose value must be specified. As we use the standard names for the elements, we don’t have to specify the string representation.

The integer value is obtained with function ord and the string representation with function$.

import std/strutils

type AsciiControlChar {.pure.} = enum NUL, SOH, STX, ETX, EOT, ENQ, ACK, BEL,
                                      BS,  HT,  LF,  VT,  FF,  CR,  SO,  SI,
                                      DLE, DC1, DC2, DC3, DC4, NAK, SYN, ETB,
                                      CAN, EM,  SUB, ESC, FS,  GS,  RS,  US,
                                      SP,  DEL = 0x7F

echo ' ', CR, " = ", ord(CR).toHex(2)
echo DEL, " = ", ord(DEL).toHex(2)
Output:
 CR = 0D
DEL = 7F

Perl

use charnames ":loose";
# There is no EM, use END OF MEDIUM.
# Do not confuse BEL with BELL. Starting in Perl 5.18, BELL refers to unicode emoji 0x1F514. ALERT is an alias for BEL.
# compile time literal
"\N{nul}\N{soh}\N{stx}\N{etx}\N{eot}\N{enq}\N{ack}\N{bel}\N{bs}\N{ht}\N{lf}\N{vt}\N{ff}\N{cr}\N{so}\N{si}\N{dle}\N{dc1}\N{dc2}\N{dc3}\N{dc4}\N{nak}\N{syn}\N{etb}\N{can}\N{end of medium}\N{sub}\N{esc}\N{fs}\N{gs}\N{rs}\N{us}\N{space}\N{delete}"
# run time
charnames::string_vianame $_;

Phix

enum nul=0, soh, stx, etx, eot, enq, ack, bel, bs, ht, lf, vt, ff, cr, so, si,
     dle, dc1, dc2, dc3, dc4, nak, syn, etb, can, em, sub, esc, fs, gs, rs, us,
     space, del = 127
?cr
?del
Output:
13
127

Raku

There doesn't seem to really be a point or a purpose to this task other than creating a enumeration...

'space' is absolutely NOT a control character. Pretending it is is just completely incorrect.

enum C0 (|(^32).map({ (0x2400 + $_).chr => $_ }), '␡' => 127);

printf "Ord: %3d, Unicode: %s, Enum: %s\n", $_, .uniname, C0($_)
   for (^128).grep: {.chr ~~ /<:Cc>/}
Output:
Ord:   0, Unicode: <control-0000>, Enum: ␀
Ord:   1, Unicode: <control-0001>, Enum: ␁
Ord:   2, Unicode: <control-0002>, Enum: ␂
Ord:   3, Unicode: <control-0003>, Enum: ␃
Ord:   4, Unicode: <control-0004>, Enum: ␄
Ord:   5, Unicode: <control-0005>, Enum: ␅
Ord:   6, Unicode: <control-0006>, Enum: ␆
Ord:   7, Unicode: <control-0007>, Enum: ␇
Ord:   8, Unicode: <control-0008>, Enum: ␈
Ord:   9, Unicode: <control-0009>, Enum: ␉
Ord:  10, Unicode: <control-000A>, Enum: ␊
Ord:  11, Unicode: <control-000B>, Enum: ␋
Ord:  12, Unicode: <control-000C>, Enum: ␌
Ord:  13, Unicode: <control-000D>, Enum: ␍
Ord:  14, Unicode: <control-000E>, Enum: ␎
Ord:  15, Unicode: <control-000F>, Enum: ␏
Ord:  16, Unicode: <control-0010>, Enum: ␐
Ord:  17, Unicode: <control-0011>, Enum: ␑
Ord:  18, Unicode: <control-0012>, Enum: ␒
Ord:  19, Unicode: <control-0013>, Enum: ␓
Ord:  20, Unicode: <control-0014>, Enum: ␔
Ord:  21, Unicode: <control-0015>, Enum: ␕
Ord:  22, Unicode: <control-0016>, Enum: ␖
Ord:  23, Unicode: <control-0017>, Enum: ␗
Ord:  24, Unicode: <control-0018>, Enum: ␘
Ord:  25, Unicode: <control-0019>, Enum: ␙
Ord:  26, Unicode: <control-001A>, Enum: ␚
Ord:  27, Unicode: <control-001B>, Enum: ␛
Ord:  28, Unicode: <control-001C>, Enum: ␜
Ord:  29, Unicode: <control-001D>, Enum: ␝
Ord:  30, Unicode: <control-001E>, Enum: ␞
Ord:  31, Unicode: <control-001F>, Enum: ␟
Ord: 127, Unicode: <control-007F>, Enum: ␡

RPL

There is no enum in RPL, but we can create global variables, each containing a specific control character, which are then available to all programs in the same directory tree.

≪ { NUL SOH STX ETX EOT ENQ ACK BEL BS HT LF VT FF CR SO SI DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US } 
   0 31 FOR ctrl
     ctrl CHR OVER ctrl GET STO
   NEXT DROP
   127 CHR 'DEL' STO 
≫ '→ASCII' STO

V (Vlang)

enum Ctrl {
    nul = 0
    soh
    stx
    etx
    eot
    enq
    ack
    bel
    bs
    ht
    lf
    vt
    ff
    cr
    so
    si
    dle
    dc1
    dc2
    dc3
    dc4
    nak
    syn
    etb
    can
    em
    sub
    esc
    fs
    gs
    rs
    us
    space
    del = 127
}

fn main() {
    println(int(Ctrl.cr))
    println(int(Ctrl.del))
}
Output:
13
127

Wren

Library: Wren-dynamic

Wren doesn't have enums built into the language but can create them dynamically at runtime. However, such enums need to have consecutive integer values.

Here, we create instead a Group which can contain any values in any order.

import "./dynamic" for Group

var names = [
    "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
    "bs",  "ht",  "lf",  "vt",  "ff",  "cr",  "so",  "si", 
    "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
    "can", "em",  "sub", "esc", "fs",  "gs",  "rs",  "us",
    "space", "del"
]

var values = (0..32).toList + [127]

var Ctrl = Group.create("Ctrl", names, values)

// print some specimen values
System.print(Ctrl.cr)
System.print(Ctrl.del)
Output:
13
127