NCL 203: Looking Inside Strings

In NCL 202, we learned how to change strings and combine them to create new ones.

Sometimes, though, we don't need to change a string.

We need to learn something about it.

For example, suppose we have:

#message "Hello!"

How many characters does that string contain?

Finding the length

SLEN finds the length of a string:

#length r0
#message "Hello!"

SLEN #length #message

D.TXT #length
D.BLT

The program displays:

6

#message is a string, but its length is a number.

SLEN examines the string and stores its result in an integer register:

"Hello!" ── SLEN ──> 6
 string              integer

This is the first time we've used a string operation to produce information that our existing integer instructions can work with.

Using the length

Once we have the length in an integer register, we can use it just like any other integer.

For example:

#length r0

#message "Hello!"
#limit 10

SLEN #length #message
BGT $long #length #limit

D.TXT "Short"
JUMP $done

$long
D.TXT "Long"

$done
D.BLT

The program displays:

Short

SLEN stores 6 in #length.

Then:

BGT $long #length #limit

asks whether that length is greater than 10.

It isn't, so execution continues downward and displays "Short".

Try changing:

#message "Hello!"

to:

#message "This is a considerably longer message."

Before running the program, predict which path it will take.

The program isn't making a decision about a number we wrote directly into an instruction. It is making a decision using information it discovered about a string.

The pattern should look familiar:

  1. Examine a value.
  2. Store the result.
  3. Compare the result.
  4. Choose a path.

We've done the same thing with calculations such as MOD. Now we're applying that pattern to text.

The empty string

A string does not have to contain any characters at all.

This:

""

is an empty string.

The two quotation marks mark the beginning and end of the string, but there is nothing between them.

Its length is therefore zero:

#length r0
#message ""

SLEN #length #message

D.TXT #length
D.BLT

The program displays:

0

That gives us a simple way to check whether a string is empty:

#length r0
#message ""

SLEN #length #message
BEQ $empty #length 0

D.TXT "Not empty"
JUMP $done

$empty
D.TXT "Empty"

$done
D.BLT

Change #message to different strings and predict which path the program will take.

Finding text inside a string

Length tells us how many characters a string contains.

Sometimes we want to know where some text appears inside it.

For that, NCL provides SFIND.

#position r0

#message "Hello, world!"
#search "world"

SFIND #position #message #search

D.TXT #position
D.BLT

The program displays:

7

SFIND searches #message for the string in #search.

If it finds a match, it stores the position where that match begins in #position.

String positions

Positions within a string begin at 0.

For:

Hello, world!

the positions look like this:

Hello, world!
0123456789012

The first character, H, is at position 0.

The w in "world" is at position 7.

So:

SFIND #position #message #search

stores:

7

in #position.

When nothing is found

What happens if the string we're looking for isn't there?

Try:

#position r0

#message "Hello, world!"
#search "goodbye"

SFIND #position #message #search

D.TXT #position
D.BLT

The program displays:

-1

SFIND returns -1 when it cannot find the requested string.

That gives us a useful distinction:

SFIND result Meaning
0 or greater Found, beginning at that position
-1 Not found

We can use that result to choose a path.

Does a string contain something?

For example:

#position r0

#message "Hello, world!"
#search "world"

SFIND #position #message #search
BEQ $missing #position -1

D.TXT "Found"
JUMP $done

$missing
D.TXT "Not found"

$done
D.BLT

SFIND finds "world" at position 7.

Since 7 is not -1, the branch is not taken and the program displays:

Found

Change:

#search "world"

to:

#search "goodbye"

Now SFIND returns -1, the branch is taken, and the program displays:

Not found

This lets the program answer a useful question:

Does this string contain this other string anywhere?

But SFIND gives us more information than simply yes or no.

It tells us where the match occurred.

Does a string start with something?

Consider:

#position r0

#message "NCS/e READY"
#search "NCS/e"

SFIND #position #message #search

SFIND returns 0.

Since position 0 is the beginning of a string, we now know that #message starts with "NCS/e".

We can test for that directly:

#position r0

#message "NCS/e READY"
#search "NCS/e"

SFIND #position #message #search
BEQ $starts #position 0

D.TXT "Does not start with "
D.TXT #search
JUMP $done

$starts
D.TXT "Starts with "
D.TXT #search

$done
D.BLT

The program displays:

Starts with NCS/e

Now change the message:

#message "READY: NCS/e"

SFIND still finds "NCS/e", but this time it finds it at position 7.

The string contains "NCS/e", but does not start with it.

And if we use:

#message "READY"

SFIND returns -1 because "NCS/e" does not appear at all.

Message SFIND result Contains NCS/e? Starts with NCS/e?
NCS/e READY 0 Yes Yes
READY: NCS/e 7 Yes No
READY -1 No No

The same SFIND result can therefore answer different questions depending on how we use it.

Combining what we know

We can combine SLEN, SFIND, and the control flow we learned in the 100-level lessons to classify a string in several ways:

#length r0
#position r1

#message "Hello from NCS/e"
#search "NCS"

SLEN #length #message
BEQ $empty #length 0

SFIND #position #message #search
BEQ $starts #position 0
BEQ $missing #position -1

D.TXT "Contains "
D.TXT #search
JUMP $done

$starts
D.TXT "Starts with "
D.TXT #search
JUMP $done

$missing
D.TXT "No match"
JUMP $done

$empty
D.TXT "Empty"

$done
D.BLT

Follow the program from the beginning.

First:

SLEN #length #message
BEQ $empty #length 0

checks whether the message is empty.

It isn't, so the program continues.

Then:

SFIND #position #message #search

searches for "NCS".

For the current message, the result is greater than 0.

That means:

BEQ $starts #position 0

is not taken, and:

BEQ $missing #position -1

is not taken either.

Execution continues downward and displays:

Contains NCS

Try changing #message to:

#message "NCS/e READY"

then:

#message "Hello!"

and finally:

#message ""

Before running each version, follow the branches and predict which message will be displayed.

Try it: ignoring capitalization

SFIND distinguishes between uppercase and lowercase letters.

Consider:

#position r0
#message s0
#search s1

SMOVE #message "Hello from NCS/e"
SMOVE #search "ncs/E"

SFIND #position #message #search

D.TXT #position
D.BLT

The capitalization of "NCS/e" and "ncs/E" does not match, so the search fails.

But we already know how to change the case of a string.

Modify the program so that the search works regardless of how the letters in #message and #search are capitalized.

You can use either SUPR or SLWR.

Remember that both strings need to be converted to the same case before SFIND compares them.

Once your program works, try changing the capitalization of either original string:

SMOVE #message "HELLO FROM ncs/E"
SMOVE #search "NcS/e"

The search should still succeed.

For an extra challenge, modify the program so that the strings stored in #message and #search remain unchanged.

You'll need two more string registers to hold the converted versions.

By combining operations we've already learned, we've made a case-insensitive search without needing a new search instruction.


SFIND can tell us where part of a string begins.

In NCL 204: Taking Strings Apart, we'll use positions like these to start extracting pieces from strings.