REALbasic Cross-Platform Application Development - page 24


Modules

Technically speaking, modules aren't object-oriented, but they are included in REALbasic because there are times when the formalisms of object-oriented programming make easy things a little harder or more tedious to do. If you're a Java programmer, you should note that as I write this, REALbasic does not have the equivalent of class methods, but modules can serve the same purpose, by making methods, properties, and constants available without having to instantiate a class.

In addition to allowing you to create your own modules, REALbasic provides some modules "free of charge," making them available to your application at all times. I want to examine one built-in module in particular to start, and then follow that up with an example of how to create a custom module.

Built-in Modules

REALbasic supplies several built-in modules, most of which I will write about in future chapters. However, one built-in module is worth discussing now. The module is called REALbasic. You really don't need to know much about this module other than it exists. The biggest impact to you, the developer, is that there are a variety of functions that are always available to you, and these functions are part of the REALbasic module. Because they are global in scope, you do not even have to refer to the REALbasic module. All you need to refer to is the function itself.

One feature of modules that differs from that of classes is that modules are always available to your application. Unlike classes (as you will see) you do not need to instantiate modules or do anything like that. If you create a module for an application, the methods and constants and properties you create will be available from the moment the program starts to the moment it stops running.

This is exactly how the REALbasic module works. It exposes a long list of functions (methods) whose scope is public and which you can call at any time.

These functions fall into two categories. One group provides various mathematical abilities, and the other group deals with strings.

Built-in Math Functions

REALbasic provides a set of built-in math functions that sometimes perform the same function as one of the operators, and some that provide additional functionality.

Trigonometry

REALbasic.Sin(aValue as Double) as Double
REALbasic.Asin(aValue as Double) as Double
REALbasic.Tan(aValue as Double) as Double
REALbasic.Atan(aValue as Double) as Double
REALbasic.Cos(aValue as Double) as Double
REALbasic.Acos(aValue as Double) as Double
 


REALbasic's trigonometry functions accept a double as the parameter and return the answer in terms of radians (double data type). This works like Visual Basic functions of the same name, with no surprises. To convert from radians to degrees, multiply radians times 180/PI.

Log, Exp

REALbasic.Exp(aValue as Double) as Double
REALbasic.Log(aValue as Double) as Double
 


Log returns the natural logarithm of a number, and Exp returns e raised to the power passed in the parameter (e is approximately equal to 2.71828).

isNumeric

REALbasic.isNumeric(anyVariable) as Boolean
 


You can pass any object or value to isNumeric to ascertain whether it represents a numeric value. Pass an object and the answer will be False, but you can also pass strings, integers, singles, and doubles and get an answer back. If you pass an integer, the answer is always true. If you pass a string, it tests to see if the string can be converted into a numeric value. This means that isNumeric will return TRue for a string like "505" but not for a string like "five". It also works with scientific notation.

Abs, Sign

REALbasic.Abs(aValue as Double) as Double
 


The Abs function takes any number as a parameter and returns the absolute value of that number. Recall that the absolute value of a number is the original number, minus its sign. It works with integers, singles, and doubles.

REALbasic.Sign(aValue as Double) as Integer
 


The Sign function tells you the sign of a number, whether it is positive or negative or zero.

Negative

-1

Zero

0

Positive

1


Max and Min

REALbasic.Max(aValue as Double, bValue as Double) as Double
REALbasic.Min(aValue as Double, bValue as Double) as Double
 


Max and Min return the highest or lowest value of a pair of numbers, respectively.

Round (Not to Be Confused with Rnd, Which Generates a Random Number)

REALbasic.Round(aValue as Double) as Double
 


The data type returned by the Round() function is always a double, even though rounding a number means, by definition, that the answer is going to be an integer. Despite the fact that it's a double, you can safely supply a variable with an integer Data type to be assigned the result of the function.

Round rounds numbers in the traditional way. If the decimal is .5 or higher, it rounds up; otherwise, it rounds down.

Ceil

REALbasic.Ceil(aValue as Double) as Double
REALbasic.Floor(aValue as Double) as Double
 


Ceil rounds up to the next highest integer, regardless of the value to the right of the decimal point. Therefore, 1.1 and 1.6 would both be rounded up to the value of 2.

Floor operates in the opposite way, rounding down regardless of the value to the right of the decimal point.

Val, CDbl

REALbasic.Val(aValue as String) as Double
 


Both Val and CDbl take a string as an argument and return the numeric value. The only difference between Val and CDbl is that CDbl takes into consideration the locale and will return the right value for strings that use characters other than . as a decimal. For example, in France a comma is used rather than a period to indicate a decimal.

Dim aNum as Integer
aNum = Val("200") // aNum equals 200
 


Pow, Sqrt

REALbasic.Pow(aValue as Double, aPower as Double) as Double
REALbasic.Sqrt(aValue as Double) as Double
 


The Pow function is the equivalent of the ^ operator. It takes an number and raises it to the power passed in the aPower parameter.

Dim result as Double
Dim root as Double
result = Pow(2, 2) // result = 4
root = Sqrt(result) // root = 2
 


Bin, Hex, Oct

REALbasic.Bin(aValue as Integer) As String
REALbasic.Hex(aValue as Integer) As String
REALbasic.Oct(aValue as Integer) As String
 


If aValue is equal to 65 (which happens to be the ASCII value of the letter A, also referred to as the decimal value), the results of these functions would be as follows:

Dim s As String
Dim i As Integer

i = 65
s = Hex(i) // s equals "41"
s = Oct(i) // s equals "101"
s = Bin(i) // s equals  "1000001"
 


Built-in String Functions

A string is a series of characters. Because the only thing a computer can deal with are numbers, the computer stores strings as numbers. The way that these numbers are mapped to refer to strings is called the string's encoding. ASCII was once a common form of encoding and it uses one byte for each string. The problem is that it limits the number of characters you can represent (256 or so). For English-speaking people, that's just fine, but it's too constraining for speakers of other languages. For years, Macintosh and Windows used different approaches to solve the encoding problem, which made it absurdly complicated to deal with text and to exchange it between the two platforms. It wasn't a problem only for Macs and Windows machinesUnix-like systems had the same problem, so the industry resolved it by establishing a new standard (or collection of standards) called Unicode. Unicode comes in different varieties: UTF-8, UTF-16, and UTF-32.

The default encoding for REALbasic is UTF-8. UTF-8 is a little smarter than the other two formats because it only uses the number of bytes that are necessary to represent a character. It represents the ASCII character set exactly like the ASCII character set does, using 1 byte. If you are using ASCII-only characters, you'll have no problems with UTF-8.

The old Macintosh default format was called MacRoman. In Windows, it was Latin-1. Both shared the common ASCII characters, but the numbers they used to represent character values higher than 127 were different.

In UTF-8, those non-ASCII values are all treated the same and are represented by 2 (or more) bytes. This means that in some cases, the number of characters in a string is the same value as the number of bytes in the string, but in other cases, it's not. This is an important distinction to understand when dealing with the global functions REALbasic provides for manipulating strings, because the encoding of the string itself sometimes impacts your approach. Another complicating factor when dealing with strings is that letters come in different cases. REALbasic is not case sensitive, which means that by default, it doesn't care about whether you use A or a. It's all the same to REALbasic. In this way, REALbasic is shielding you from some of the underlying complexity of dealing with strings. Even though REALbasic doesn't care whether you use A or a, the number the computer uses to represent the uppercase A is different from the one used to represent the lowercase a.

Although REALbasic doesn't care, you sometimes care, so REALbasic provides you with ways to search or manipulate strings in case-sensitive ways, too.

I have already reviewed comparison operators and how they work with strings. The problem, or limitation, with operators are that they are not case sensitive and you have no way of changing that. Although they may be convenient to type, they may not always be appropriate. To address this, the following functions provide the same functionality as the operators, plus some, including the capability to make comparisons that are case sensitive.

Most of these functions have two versions. When there are two versions, the second version's name always ends with a B to signify binary, which means that it treats the string as binary data rather than as text.

What this means, in practice, is that the standard or normal version of this function treats the string like text, which mean that each letter is counted once; REALbasic doesn't differentiate between uppercase and lowercase letters. When treated as binary, REALbasic pays attention to the actual binary values. The practical implication in most cases is that it makes the function case sensitive. A secondary side-effect of case sensitivity is that it doesn't differentiate between characters encoded in 1 byte versus those encoded to 2 or 4 bytes.

I'll point out the different results as I review each function.

The functions are also callable as global functions, like the math functions in the previous section, but you can also call them as if they were functions of string. This is one of those inconsistencies implemented in REALbasic for convenience, but which has the potential for creating confusion in someone trying to understand the difference between a data type and an object. Normally, a scalar data type, like a string, would not and could not have methods, but strings, like Variants, do have methods you can call.

Again, these should all be very familiar to Visual Basic programmers.

StrComp

REALbasic.StrComp(aString as String, bString as String, Mode as Integer) as
Integer

aString.StrComp(bString as String, Mode as Integer) as Integer
 


StrComp compares two strings to see if they are equal. Now, having said that most of these functions that operate on strings have two versions, I start off with one which has only one. You do, however, have control over case sensitivity be setting the mode of the function.

Dim aString as String
Dim bString as String
Dim mode as Integer
Dim result as Integer
mode = 1
aString = "Aaron"
bString = "aaron"

result = StrComp(aString,bString, mode)
 


In this example, result is equal to 0 because we used a mode of 1, which is the mode used when you do not care about case (it's also called the lexicographic mode). Using the same example, but setting mode equal to 0 will cause the result to be -1. The uppercase A has an ASCII value of 65, which is lower than the ASCII value of a" which is 97. Uppercase values are always lower than lowercase values.

When you used the arithmetic operators on strings, a Boolean value was returned, but when using StrComp, an integer is returned and the answer can be one of three values.

Here are the equivalent responses for each type. Assuming the value in the left column is true, the value in the right column indicates the value returned by StrComp.

a<b

-1

a=b

0

a>b

1

a<=b

-1, 0

a>=b

0, 1


The point of this is to remind you that StrComp doesn't offer exactly the same set of features as the operators do. If you want to establish that a string is less than or equal to a value, StrComp will return either -1, or 0.

InStr, InStrB

REALbasic.InStr([startPosition as Integer], sourceString as String, searchString
as String) As Integer

aString.InStr([startPosition as Integer], searchString as String) as Integer
 


Sometimes you need to find out if a character or a series of characters exist inside a string, and the InStr function can help you do this.

InStr is used to find a substringto see if a string of characters exists within another string.

result = InStr(sourceString, searchString)
 


InStrB searches binary data, not strings. This is important when using Unicode, because some strings are represented by multiple bytes. If you are searching Unicode text and you are using InStrB, you will get the byte position of where the value was found, and not the character position.

Here's an example of the impact this has when searching for a character in a string that contains the Unicode character "©".

Dim s as String
Dim position as Integer

S = "© Copyright"

position = InStr(s, "C") // position equals 3
position = InStrB(s, "C") // position equals 4
 


When searching with the normal InStr, the function returns a position of 3, because "C" is the third character in the string. However, when using InStrB, the value returned is 4 because "©" takes up 2 bytes rather than 1 byte. Remember that we are using UTF-8 in these examples because that is the default encoding in REALbasic. If the string were encoded in UTF-16, where all characters are encoded in two bytes, the answer would be 6.

Earlier, I showed an example of how to search for the position of a character within an array of characters. The functionality of that example mimics the functionality of InStr. One reason you would use InStr would be to test to see if a character was included in another string. Now the string could be anything you wantit doesn't actually have to be a word or anything like that.

The following example searches through an array of characters to find the first punctuation mark. In this case, I am using InStr to test to see if one character belongs to a set of characters which, in this case, are characters used in punctuation.

Dim punct as String
Dim paragraph as String
Dim isPunct as Integer
Dim position as Integer
Dim chars(-1) as String
Dim x,y as Integer

punct= ".,?!;:[]{}()"
paragraph="This is a paragraph. It has two sentences!"
chars = Split(paragraph, "")

y = Ubound(chars)
For x = 0 to y
 isPunct = InStr(punct, chars(x))
If isPunct > 0 then
position = x // position equals 19
Exit
End If
Next
 


Using the same basic concept, you can use a combination of Arrays and InStr to split a sentence or paragraph into words.

Dim chars(-1) as String
Dim char_buffer(-1) as String
Dim word_buffer(-1) as String

Dim sentence as String
Dim whitespace as String
Dim pos as Integer
Dim x,y as Integer

whitespace = " " + chr(10) + chr(13)
sentence = "There are four words."

chars = Array(sentence, "")
y = Ubound(chars)

For x = 0 to y
  pos = whitespace.InStr(chars(x))
  If pos > 0 Then
    word_buffer.append(join(char_buffer,""))
    ReDim char_buffer(-1)
  Else
    char_buffer.append(chars(x))
  End if
Next
 


When this is run, the word_buffer Array has four elements, as follows:

word_buffer(0) = "There"
word_buffer(1) = "are"
word_buffer(2) = "four"
word_buffer(3) = "words."
 


You should notice that it leaves the period at the end of "words.", but it would be a simple matter of including punctuation characters in the list of characters to test for. This process is often called tokenizing, and it can be useful in a lot of situations.

Mid

REALbasic.Mid(sourceString as String, StartPos as Integer) as String
REALbasic.Mid(sourceString as String, StartPos as Integer, Length as Integer)as String

REALbasic.MidB(sourceString as String, StartPos as Integer)as String
REALbasic.MidB(sourceString as String, StartPos as Integer, Length as Integer) as String
 


Mid and MidB are used to copy a portion of one string into another. You specify the string from which to copy, the position from which to start copying, and, if applicable, the length of text to be copied. If the length value is omitted, it copies the entire string from the start position onward. The first character is numbered 1.

Dim aString as String
Dim result as String

aString = "Cow."
result = Mid(aString, 2, 2) //result equals "ow"
 


Left

result = Left(sourceString, Length)
result = LeftB(sourceString, Length)

result = aString.Left(Length)
result = aString.LeftB(Length)
 


Left, like Mid, finds a string within a string. Unlike Mid, the start position for Left is always the start of the string, and you have to define the length of the substring.

Right

result = Right(sourceString, Length)
result = RightB(sourceString, Length)

result = aString.Right(Length)
result = aString.RightB(Length)
 


Use Right to copy a substring of a specific length, starting from the right side of the string.

Asc

REALbasic.Asc(aCharacter as String) as Integer
REALbasic.AscB(aCharacter as String) as Integer
 


The following example compares the results of using Asc and AscB on three different characters. The first is a character from the ASCII character set, the lowercase "a", the others are characters used in UTF-8, the default encoding for REALbasic strings.

Dim aString, bString, cString as String
Dim result as Integer

aString = "a"
bString = "©"
cString = ""

result = aString.Asc // result = 97
result = aString.AscB // result = 97
result = bString.Asc // result = 169
result = bString.AscB // result = 194
result = cString.Asc // result = 8364
result = cString.AscB // result = 226
 


The answer for "a" is the same whether you are using Asc or AscB because "a" is an ASCII character and can be represented in 7 bits. The "©" character has a different value depending on whether you use Asc or AscB. Even though it can be represented in one byte, the reason it is different from the raw ASCII value is that it uses all 8 bits in the byte, whereas ASCII characters only use 7. The character " " requires two bytes to represent it. Asc returns the code point for the character, whereas AscB just returns the first byte.

Chr

REALbasic.Chr(aCodePoint as Integer) as String
REALbasic.ChrB(aCodePoint as Integer) as String
 


Chr takes a code point and returns a character as a string. It's the inverse of Asc.

Dim aString as String
aString = Chr(97) //aString equals "a"
 


Len

REALbasic.Len(aString)as Integer
REALbasic.LenB(aString)as Integer
 


Len counts the length of a string in terms of characters. LenB returns the length of a string in terms of bytes. For non-ASCII characters, this means that Len and LenB return different values.

Dim aString, bString, cString as String
Dim result as Integer
 


aString = "a"
bString = "©"
cString = " "
result = aString.Len() // result = 1
result = aString.LenB() // result = 1
result = bString.Len() // result = 1
result = bString.LenB() // result = 2
result = cString.Len() // result = 1
result = cString.LenB() // result = 3
 


In this example, you can see that using Len for the © returns a value of 1, whereas LenB returns a value of 2. Note that you can also use the global function for Len and LenB as follows:

result = Len(bString) // result = 1
result = LenB(bString) // result = 2
 


Str, CStr

REALbasic.Str(aNumber as Double) as String
REALbasic.Cstr(aNumber as Double) as String
 


Str and CStr return a string representation of a number. CStr is locale aware and will properly observe regional variations in the display of numbers. Both return the numbers in regular decimal format until the number grows too large, then it switches to scientific notation. The point the change occurs varies for integers and floating-point numbers. The following numbers are the first numbers that will be returned in scientific notation.

Integers: 1,000,001

Singles/Doubles: 1,000,000.0

Dim i,j as Integer
Dim s,t as String

i = 1000000
j = 1000001

s = Str(i) // s = "1000000"
t = Str(j) // s = "1.000001e+6"
 


Format

REALbasic.Format(sourceString as String, format as String) as String
 


In many cases, the strings returned by Str and CStr will be good enough, but if you need to have the strings formatted in a particular way, you should use Format. It is extremely flexible.

Placeholder

Description

#

Display a digit if present. If the digit is not present, nothing is displayed. If you use the format string ##### for the number 3, only the number 3 will show up.

0

Displays a digit if present, 0 if not present. This can be used to force a floating-point number to display a fixed number of places after the decimal point. Contrast this with #. If you use 00000 for the number 3, then 00003 will be displayed.

.

Displays the decimal point. Therefore,#.00 always displays the number with two numbers to the right of the decimal point. The number 100 is displayed 100.00.

,

Displays the thousands separator. Best used in conjunction with #. If you are expecting values from 0 to 1,000,000, use #,###,### for the format string because # doesn't display if there is not a digit for it.

%

Displays the number as a percentage, meaning it is multiplied by 100 and followed by the % character (with no space in between).

(

Displays open paren.

)

Displays close paren.

+

Displays the sign of the number, showing + if the number is positive and - if the number is negative.

-

Displays a - when a number is negative, and nothing when a number is positive.

e

Displays the number using scientific notation. You place it immediately following the number, not before it. Used in conjunction with # and ., which specify how many decimal places to show. It defaults to +, but you have the option of using e+ for clarity. It is required for negative.

\{character}

The escape character \ causes the following character to be displayed as is. Use it to place a dollar sign in front of currency: \$##.00.


The formatting string can consist of three different formatting patterns, separated by a semicolon. The first item applies to positive numbers, the second to negative numbers, and the third to the number zero.

Here are some examples of common formats:

Drop the values after the decimal point:

Dim d as Double
Dim s as String
d = 100.1

s = Format(d, "#") // s = "100"
 


Avoid using scientific notation (in contrast to using Str):

Dim i as Integer
Dim s,t as String
i = 1000001

s = Format(i, "#.###") // s = "1000001."
t = Str(i) // t = "1.000001e+6"
 


Control how scientific notation is displayed:

Dim d as Integer
Dim s,t as String
d = 134000

s = Format(d, "#.##e+") // s = "1.34e+5"
t = Format(d, "#.#e+") // s = "1.3e+5"
 


Display currency in a way that always shows the number of cents:

Dim d as Double
Dim s as String
d = 100
s = Format(d, "\$#.00") // s = "$100.00"
 


Display currency so that negative values are surrounded by parentheses (somewhat like the accounting format in Microsoft Excel):

Dim d as Double
Dim s as String
d = -1003
s = Format(d, "\$#,###.00;\$(#,###.00)") // s = "$(1,003.00)"
 


Display a 10-digit telephone number properly:

Dim d as Double
Dim s as String
d = 8005551212
s = Format(d, "(###)\ ###\-####") // s = "(800) 555-1212"
 


Note that spaces are ignored unless you escape them with \, just like any other character. Also, if you pass this format a number that is longer than 10 digits, Format displays those numbers within the parentheses. Using the preceding example, if I equaled "80055512120", the value for s would be "(8005) 551-2120". This is true whether you use "#" or "0" (and both work for this, by the way).

Trim, RTrim, LTrim

REALbasic.Trim(sourceString as String) as String
REALbasic.RTrim(sourceString as String) as String
REALbasic.LTrim(sourceString as String) as String
 


Trims whitespace on strings. trim removes whitespace on both sides of the string. RTrim and LTrim trim only the right or left side of the string, respectively. Whitespace characters are include spaces, tabs, and end-of-line characters.

Dim aString as String
Dim newString as String
aString = " myString"

newString = Trim(aString) // newString = "myString"
newString = RTrim(aString)// newString = "  myString"
newString = LTrim(aString) // newString = "myString"
 


UpperCase, LowerCase, TitleCase

REALbasic.UpperCase(sourceString as String) as String
REALbasic.LowerCase(sourceString as String) as String
REALbasic.TitleCase(sourceString as String) as String
 


UpperCase and LowerCase convert a string to all lowercase or uppercase characters, respectively. TitleCase capitalizes the first letter of each word in a string. Computer programmers are often under the impression that it is proper to capitalize the first letter of each word in a string when representing a title, but that's not the case, so don't use this to capitalize the title of the English term paper you have to turn in because English teachers don't see it that way. Not every word in a title gets capitalized, so use it at your own discretion.

CountFields

REALbasic.CountFields(sourceString as String, delimiter as String) as Integer
REALbasic.CountFieldsB(sourceString as String, delimiter as String) as Integer
 


CountFields divides up a string into fields based on a certain delimiter. CountFieldsB treats the delimiter as binary data, which is most commonly useful when considering case when determining the delimiter (for example, when you want to split the string on "a" and not "A").

Dim i as Integer
i = CountFields("a*b", "*") // i = 2
 


It also counts empty (null) fields.

Dim i as Integer
i = CountFields("a*b*", "*") // i = 3
 


NthField

CountFields is almost always used in conjunction with NthField. NthField returns a string based on the field position of the string.

Dim s as String
s = NthField("a*b", "*", 2) // s = "b"
 


Much of the functionality of the CountFields/NthField functions can now be better managed with the Split, Join, and Array functions used with Arrays (see the section "Arrays").

The reason I usually avoid CountFields and NthField for this kind of activity is that arrays make it easier to iterate through a string with many fields, and it also seems to be a little more efficient. Compare the two techniques:

Dim s as String
Dim anArray() as String
anArray = Split("Some really long string", " ")


For Each s in anArray
    // do something with s;
Next