NCL 207: Special Characters
So far, most of the characters in our strings have been written directly into the program.
If we want:
Hello!
we can write:
D.TXT "Hello!"
D.BLT
But some characters are difficult to write directly inside a string.
Others aren't normally visible at all.
NCL uses escape sequences to represent these characters.
Putting quotes inside a string
Suppose we want to display:
She said "Hello!"
We might try:
D.TXT "She said "Hello!""
But there's a problem.
The quotation mark already has a job in NCL: it marks the beginning or end of a string literal.
We need a way to tell NCL:
This quotation mark is part of the string. It does not end it.
We can do that by putting a backslash before it:
D.TXT "She said \"Hello!\""
D.BLT
The program displays:
She said "Hello!"
Inside a string:
\"
represents one quotation mark.
The combination of the backslash and the character following it is called an escape sequence.
What about the backslash?
We've just given \ a special meaning inside a string.
That creates another problem.
What if we actually want to display a backslash?
Suppose we want:
SYSTEM\STARTUP
A backslash written by itself could be interpreted as the beginning of an escape sequence.
So the backslash has an escape sequence of its own:
\\
For example:
D.TXT "SYSTEM\\STARTUP"
D.BLT
displays:
SYSTEM\STARTUP
Two backslashes in the source represent one backslash in the string.
So far, escape sequences have let us write visible characters that would otherwise be awkward to include.
They can also represent characters that aren't visible at all.
Moving to another line
Consider:
D.TXT "Hello\r\nWorld"
D.BLT
The display shows:
Hello
World
There are two escape sequences between the words:
\r
and:
\n
They represent two different characters.
\r is carriage return. On the Display, it moves the cursor to the beginning of the current row.
\n is line feed. It moves the cursor down one row.
Together:
\r\n
moves to the beginning of the next row.
This distinction is easier to see if we leave one of them out.
Line feed is not carriage return
Try:
D.TXT "ONE\r\nTWO\r\nTHREE"
D.BLT
The result is:
ONE
TWO
THREE
Each carriage return moves the cursor back to the beginning of its row, and each line feed moves it down.
Now remove the carriage returns:
D.TXT "ONE\nTWO\nTHREE"
D.BLT
Line feed only moves the cursor down.
It does not move it back to the left.
The result therefore forms a staircase:
ONE
TWO
THREE
This is why \r and \n are separate characters.
They perform separate operations.
Other control characters
Several other escape sequences represent characters that the Display interprets as controls.
| Escape | Name | Display effect |
|---|---|---|
\r |
Carriage return | Move to the beginning of the current row |
\n |
Line feed | Move down one row |
\t |
Horizontal tab | Move to the next tab stop |
\b |
Backspace | Move back one character |
\f |
Form feed | Move the cursor to (0,0) |
\d |
Delete | Blank the character at the cursor without moving it |
\a |
Bell | Sound a beep |
These characters are part of the string even though they don't normally appear as visible glyphs.
Moving backward
Backspace deserves a closer look.
Consider:
D.TXT "ABC\bX"
D.BLT
First, the Display writes:
ABC
The cursor is now after the C.
Then:
\b
moves the cursor backward onto the C.
It does not erase it.
Finally, X is written at the new cursor position, overtyping the C.
The result is:
ABX
So backspace changes where the next character will be written.
It does not itself change the character already on the Display.
Blanking a character
Delete, written as:
\d
does change the Display.
It replaces the character at the cursor with a space without moving the cursor.
That distinction becomes useful when we don't know whether anything will be written afterward.
For example:
#replacement s0
SMOVE #replacement ""
D.TXT "ABC"
D.TXT "\b\d"
D.TXT #replacement
D.BLT
First:
D.TXT "ABC"
writes:
ABC
Then:
D.TXT "\b\d"
does two things:
\bmoves the cursor backward onto theC.\dblanks theCwithout moving the cursor.
At that point, the Display contains:
AB
and the cursor is still sitting on the blank position.
Finally:
D.TXT #replacement
writes the contents of #replacement.
But #replacement contains:
""
so there is nothing to write.
The final result remains:
AB
Now change:
SMOVE #replacement ""
to:
SMOVE #replacement "X"
The same display code now produces:
ABX
The cursor movement and deletion didn't change.
Only the runtime value we wrote afterward did.
This gives us a useful distinction:
\bchanges where the next write occurs.\dchanges what is currently displayed there.
Tabs and form feed
A horizontal tab:
\t
moves the cursor to the next tab stop.
For example:
D.TXT "NAME\tSCORE"
D.BLT
can be useful when displaying values in columns.
We'll do much more with positioning and laying out the Display later.
Form feed:
\f
moves the cursor to position (0,0).
That's the top-left position of the Display.
Like the other control characters, it doesn't need to produce a visible glyph to affect what happens next.
Making a sound
One control character doesn't move the cursor or draw anything.
Bell:
\a
sounds a beep.
For example:
D.TXT "Attention!\a"
D.BLT
displays:
Attention!
and sounds the bell.
The bell character itself isn't visible.
Its effect comes from how the Display interprets it.
What's actually in the string?
There is an important difference between how an escape sequence is written in NCL source and the string that the program actually receives.
Consider:
#message s0
#length r0
SMOVE #message "A\nB"
SLEN #length #message
D.TXT #length
D.BLT
What value should #length contain?
The source and resulting string are not the same sequence of written symbols:
| Contents | |
|---|---|
| NCL source | "A\nB" |
| Resulting string | A, LF, B |
| String length | 3 |
\n represents one character: line feed.
The backslash and n are how we write the line feed in NCL source.
They do not remain as two separate characters in the resulting string.
The same applies to:
\"
\\
\r
\t
\a
and the other escape sequences.
Each is a way of representing a character inside an NCL string literal.
Characters and their effects
There is one more distinction worth making.
NCL turns:
\n
into a line feed character.
The line feed character itself does not mean:
Tell the Display to move down.
It is simply a character in the string.
When that string is sent to the Display, the Display recognizes line feed and moves its cursor down.
The same string could be stored, inspected, combined with other strings, or eventually sent somewhere else.
What a control character does depends on what receives it.
For example:
#message s0
#length r0
SMOVE #message "A\nB"
SLEN #length #message
doesn't move the Display cursor at all.
It only stores a string and measures its length.
The control character affects the Display when we actually send the string there:
D.TXT #message
D.BLT
Keeping these two ideas separate will become increasingly useful:
- An escape sequence in NCL source represents a character.
- Something receiving that character may give it a particular meaning.
Try it
Predict what this program will display before running it:
D.TXT "ONE\r\nTWO\r\nTHREE"
D.BLT
Then try:
D.TXT "ONE\nTWO\nTHREE"
D.BLT
and:
D.TXT "ONE\rTWO\rTHREE"
D.BLT
For each version, follow the cursor in your mind:
- What gets written?
- Where is the cursor afterward?
- What does the next control character change?
Then experiment with:
#replacement s0
SMOVE #replacement ""
D.TXT "READY"
D.TXT "\b\d"
D.TXT #replacement
D.BLT
Try different values for #replacement, including:
SMOVE #replacement "!"
SMOVE #replacement "?"
and:
SMOVE #replacement ""
Predict the final display each time before running the program.
Escape sequences give us a convenient way to represent characters that are difficult, impossible, or ambiguous to write directly in an NCL string.
In NCL 208: Character Codes, we'll look at another way to represent characters: by number.