Task 11: String Manipulation  

Contents

The UCASE$ and LCASE$ Statements
The LTRIM$, RTRIM$, and _TRIM$ Statements
The INSTR Statement
The STR$ and VAL Statements
The LEN Statement
The RIGHT$, LEFT$, and MID$ Statements
The ASC and CHR$ Statements
The STRING$ and SPACE$ Statements
The SWAP Statement
A Quick String Manipulation Demo
Your Turn - LetterCount.BAS
Command Reference



Not all games involve pressing buttons to shoot enemies on the screen. Many of the all time favorites involve entering text to play such as Hangman, Scrabble, crossword puzzles, Boggle and Text Twist just to name few. Luckily QB64 has a rich feature set of commands that allow the programmer to manipulate text entry in many different ways.


The UCASE$ and LCASE$ Statements


When a user answers a program's question, for instance, "What is the capital of Ohio?", how will the user answer? Will the user type in the correct answer? Will the answer be in UPPER or lower case or a cOmbInaTion of both? How do you as the programmer check for all of these variations? The answer lies in turning the user's response into something that can easily be checked. Type the following program in, save it as UCASE.BAS, and then execute it.



As long as the user knows how to spell Columbus the case of the response does not matter. The UCASE$ statement in line 14 returns the string parameter associated with it in all upper case. The LCASE$ statement does just the opposite, returning the string parameter associated with it in all lower case.  Line 14 could have just as easily been:

IF LCASE$(Answer$) = "columbus" THEN

and the program would behave just as it did using UCASE$.



The LTRIM$, RTRIM$, and _TRIM$ Statements


User's do funny things like not enter responses as we programmers expect. You always have to take into account the different ways a response can be entered in the WRONG way by a user for your code to be effective. The example code above will fail if the user enter spaces (presses the space bar) either before or after the word Columbus. The spaces will be seen as legitimate characters typed in by the user.

There are three statements that will remove leading, trailing, and both leading and trailing spaces from a string. LTRIM$ removes leading, or left hand, spaces from a string. RTRIM$ removes trailing, or right hand, spaces from a string, and _RTRIM$ removes both leading and trailing spaces from a string. If you change line 14 in the example code to:

IF _RTRIM$(UCASE$(Answer$)) = "COLUMBUS" THEN

Answer$ will first be converted to upper case and then have both leading and trailing spaces removed from it. Yes, this new line of code looks a bit complicated because one statement is embedded into another. This is a very common thing to do in programming that enables multiple actions to be taken on a parameter at the same time. Simply follow the order of operations to see how the line of code operates. UCASE$ falls inside of _TRIM$'s parenthesis so the UCASE$ statement is acted upon first. Once UCASE$ has returned the upper case form of Answer$ it's _TRIM$'s turn to take that upper case Answer$ and remove the leading and trailing spaces. You now have a string returned that is in both upper case and has had the leading and trailing spaces removed.

You could even change the line of code to read:

Answer$ = _TRIM$(UCASE$(Answer$))
IF Answer$ = "COLUMBUS" THEN


to permanently modify Answer$ by making the changes and then placing those changes back into Answer$ itself. This is handy if you need to reference Answer$ again later in the code as it will save you from having to test for upper case and leading/trailing spaces again.



The INSTR Statement


The previous examples work great for single word answers but those darn users will always test the limits of your code. What if the user were to answer "I think it's Columbus?" Technically the answer has the correct response embedded in the string the user entered but how do we see that in code?

The INSTR statement has the ability to search a base string with a defined search string and return a numeric value of where the search string was found within the base string. Once again let's modify the previous example code to see this in action. When you execute the program type your answer in as "I believe Columbus would be the answer" and see if you are correct.





Figure 1 - This is how Skynet got its start!

INSTR requires a string to search called the base string. In this case the base string is the upper case result of UCASE$, so the entire response entered is converted to upper case. The next parameter that INSTR requires is a string to search for called the search string. In line 14 we supplied INSTR with a search string of "COLUMBUS". If the string "COLUMBUS" is found anywhere within the user's response INSTR will return a numeric value of where it was found. If INSTR is anything other than zero then "Correct!" will get printed since we know that the IF...THEN statement will consider any numeric value other than zero as true.

There is one strange behavior of INSTR that needs to be pointed out. The following line of code will result in a value of 1 being returned.

Location% = INSTR("ABCDE", "") ' null string returns a value of 1

A search for a null string ( "" ) will always result in a positive value being returned (unless you search for a null string within a null string). This may sound like a bug but in actuality every string does contain a null string. You just need to be aware of this behavior if you start receiving a result you did not expect.


INSTR also has another trick up its sleeve. It can find multiple occurrences of the search string in the base string and report back where it finds all of them. Type in the following example to see how this works. Save the code as InstrDemo.BAS when finished.




Figure 2 - The Weather in Spain Seems Nice

The INSTR statement can accept an optional position parameter as seen in line 20 of the code.

Position% = INSTR(Position% + 1, Phrase$, Search$)

Each time an instance of "ain" is found that position is recorded so the next time around Position% + 1 can be used to resume the search through the remainder of the base string. This continues on until Position% becomes zero, meaning no more matches, and ending the loop.



The STR$ and VAL Statements


There will be times when you need to convert a string to a numeric value and a numeric value to a string. The STR$ statement is used to convert a numeric value to a string. The VAL statement is used to convert a string to a numeric value.

LINE INPUT "Enter a number between 1 and 10 > ", Number$
Value! = VAL(Number$) ' convert string to a numeric value


The above example shows a number being asked for however it's being saved in a string variable. The VAL statement is used to convert Number$ into an actual numeric value and then saved into the single type variable Value!. If the characters in Number$ are non-numeric, such as "Hello World", VAL will simply return a numeric value of zero.

INPUT "Enter a number between 1 and 10 > ",Value%
Number$ = STR$(Value%) ' convert numeric value to a string


In this example the opposite is being performed. The numeric value contained within Value% is being converted to a string and then saved into Number$. Positive numeric values converted to strings will always contain a leading space. The space is there for the possibility of a negative value that includes a minus sign. For example:

PRINT "*"; STR$(10); "*" '  * 10* printed to the screen
PRINT "*"; STR$(-10); "*" ' *-10* printed to the screen


You'll need to use either LTRIM$ or _TRIM$ to remove the leading space if you do not wish it to be present.

Number$ = LTRIM$(STR$(Value%)) ' no leading space



The LEN Statement


The LEN statement returns the number of characters contained in a string effectively reporting its length.

Phrase$ = "The rain in Spain falls mainly on the plain."
PRINT LEN(Phrase$) ' 44 is printed to the screen



The RIGHT$, LEFT$, and MID$ Statements


These statements are used to parse, or break apart, a string into smaller pieces. An example of this is when you use the TIME$ and DATE$ statements to retrieve the time and date from QB64. TIME$ delivers the time in the string form "HH:MM:SS" and DATE$ in the string form "MM-DD-YYYY". In order to get the individual hours, minutes, and seconds from TIME$, and the individual month, day, and year from DATE$ you'll need to to use the LEFT$, RIGHT$, and MID$ statements. Type the following example program into your IDE to show this. Save the code as ParseDemo.BAS when finished.




Figure 3 - Time to Make the Donuts!

The LEFT$ statement is used to grab a predetermined number of characters starting at the left-hand side of the string. In line 30 of the program LEFT$ is used to parse just the month portion of DATE$.

Month% = VAL(LEFT$(DATE$, 2)) ' parse MM from MM-DD-YYYY

A statement within a statement within a statement! WooHoo, as I stated before this is a common occurrence in programming. Let's break it down.

DATE$ returns a string in the form of "MM-DD-YYYY". Then we plug that string into LEFT$:

LEFT$("MM-DD-YYYY", 2) ' get the first two left hand characters from MM-DD-YYYY

LEFT$ grabbed the first two characters of the string which equals the month. In our example this would be "04" or April. The VAL statement is now used to convert that string into an actual numeric value:

VAL("04") ' convert to a true numeric value

LEFT$ was also used in the same manner on line 33 to get the current hour from TIME$.

Hours% = VAL(LEFT$(TIME$, 2)) ' parse HH from HH:MM:SS

In line 31 the MID$ statement is used to parse out a string from within DATE$:

Day% = VAL(MID$(DATE$, 4, 2)) ' parse DD from MM-DD-YYYY

MID$ starts at position 4 of the string and then parses 2 characters starting at that position.

And finally the RIGHT$ statement is used  to parse out the remaining date in line 32:

Year% = VAL(RIGHT$(DATE$, 4)) ' parse YYYY from MM-DD-YYYY

Here is another example program that uses string manipulation to create a scrolling LED sign. Due to the length of this example the source code has been included entitled LEDSign.BAS so you can simply load it into your IDE if you wish. As always however I encourage you to take the time to type this program in by hand.





Figure 4 - A Scrolling LED Sign

Don't let the length of this source code scare you. Take each line one at a time an you'll see there very little that is new here from what we've already covered. The statements that are new we'll get to shortly. Source code such as this around 250 lines long is nothing when compared to a full featured game, of which many I've written in QB64, at around 3000 lines of code or more. If you stick with programming you'll be busting out code this length and longer on a regular basis.


The ASC and CHR$ Statements


The CHR$ statement is used to print any value from 0 to 255 that corresponds to a character within the ASCII table. Typically CHR$ is used to print characters that are not accessible through the keyboard through standard means. One example would be playing card suits that are in the ASCII table.

PRINT CHR$(3) ' ♥ heart symbol
PRINT CHR$(4) ' ♦ diamond symbol
PRINT CHR$(5) ' ♣ club symbol
PRINT CHR$(6) ' ♠ spade symbol


The ASC statement does just the opposite and returns the ASCII numeric value of a character passed to it.

PRINT ASC("A") ' 65 printed to the screen
PRINT ASC(" ") ' 32 printer to the screen


This example program shows how the ASC and CHR$ statements can be used together to identify keystrokes. Save the code as CHR_ASC.BAS when finished typing it in.





The STRING$ and SPACE$ Statements


When you need a lot of the same character in a row the STRING$ statement has you covered. Simply supply STRING$ with a numeric value and a character like so:

PRINT STRIN$(80, "*")  ' 80 asterisks will be printed to the screen

You can also supply STRING$ with the ASCII numeric value of a character.

PRINT STRIN$(80, 42) ' 80 asterisks will be printed to the screen

The STRING$  statement comes in handy when building text screen boxes by using the extended ASCII characters provided to do so. Type in the following example and save it as ASCIIBoxes.BAS when complete.





Figure 5 - Old School ASCII Boxes

The SPACE$ statement is used to generate spaces of a requested length.

PRINT SPACE$(80) ' 80 spaces printed to the screen



The SWAP Statement


The SWAP statement is used to switch values between two numeric or string values. The variables to be swapped must be of the same type. The following example shows the SWAP statement in action. Save the code as SwapDemo.BAS when completed.




Figure 6 - Variable Swapping


A Quick String Manipulation Demo


Here is a sample program that uses string manipulation to create a hidden password function. Save the code as HiddenPassword.BAS when finished typing it in.




Figure 7 - Don't Peek!


Your Turn


Create a program that scans a user supplied sentence and counts the individual letters found within the sentence. Figure 8 below shows how the program should execute.


Figure 8 - Counting Letters

- Each letter in the sentence should be counted regardless of being upper or lower case.
- No graphics screen is needed. Program should run in standard text screen.
- Save the program as LetterCount.BAS when finished.

Click here to see the solution.


COMMAND REFERENCE


New commands introduced in this task:

UCASE$()
LCASE$()
LTRIM$()
RTRIM$()
_TRIM$()
INSTR()
STR$()
DATE$
LEFT$()
RIGHT$()
MID$()
ASC()
STRING$()
SPACE$()
SWAP


New concepts introduced in this task:

parse



Commands learned in Task 10:

DIM
TYPE
END TYPE
AS
REDIM
_PRESERVE
UBOUND
LBOUND
OPTION BASE


Concepts learned in Task 10:

array
one dimensional array
element
index
two dimensional array
three dimensional array
Dynamic Array
Static Array




Commands learned in Task 9:

LINE INPUT
INKEY$
CHR$()
_KEYDOWN
_KEYHIT
_MOUSEX
_MOUSEY
_MOUSEINPUT
_MOUSEHIDE
_MOUSESHOW
_MOUSEMOVE
_MOUSEBUTTON


Concepts learned in Task 9:

BIOS
ASCII
buffer
CMOS
ROM
ASCII chart



Commands learned in Task 8:

SUB
END SUB
FUNCTION
END FUNCTION
SHARED

Concepts learned in Task 8:

subroutine
function
local variables
global variables
Pythagorean Theorem



Commands learned in Task 7:

SCREEN
LINE
CIRCLE
PAINT
PSET
_RGB32()


Concepts learned in Task 7:

pixels
radian
counter-clockwise
aspect ratio


Commands learned in Task 6:

FOR...NEXT
STEP
CLS
SLEEP
_DELAY
SYSTEM
DO...LOOP (and variations)
WHILE...WEND

Concepts learned in Task 6:

frame rate
controlled loops
conditional loops


Commands learned in Task 5:

TIME$
VAL()
IF...THEN
AND
END
GOTO
SELECT CASE...END SELECT
CASE
TO
IS
CASE ELSE
colon ( : )

Concepts learned in Task 5:

relational operators
functions
nesting
order of operations
Boolean
conditions
indenting
loops
labels


Commands learned in Task 4:

INPUT
DIM
REM or '
CONST

Concepts learned in Task 4:

variables
type declarations
literal strings
concatenation
integers
long integers
single precision
double precision
strings
null strings
reserved words
operators ( +, -, *, / )
declare
constant


Commands learned in Task 3:

PRINT

Concepts learned in Task 3:

execute
statement
expression
literal string
syntax error