AP Computer Science Mr. Haytock Unit 3: Strings Topics: I. Strings II. Object references III. String methods Materials: I. String overview II. String exercises #1 III. String exercises #2 IV. Morphology Specifications V. Mad Libs Specifications 1 String Overview A proper implementation of how computers manipulate text data requires us to have an in-depth understanding of how computers manage “strings.” You can think about a string as a series of characters. Characters include letters, numbers, spaces and punctuation. Strings are a predefined class of the Java language. Do not confuse them with the primitive types (int, double, boolean, etc). Strings are objects. Objects contain data (like the characters in a string) but they also include methods (ways to manipulate the data in the string). The idea of associating data with the methods that work upon it is called encapsulation. A. Declaring an object of type String When you declare a String by using the type and identifier, if you don’t assign it a value right away, no memory is allocated to the String at that time. The identifier is just a reference to a string that does not yet exist. Essentially, you are naming something before constructing it. //Empty strings String name1; String schoolName, movieTitle; However, you can give a String a value later in your program. When the String is initialized, memory is allocated for it. The amount of memory assigned to a String depends on how many characters are in it. name1 = “Harry Potter”; schoolName = “Hogwarts”; movieTitle = “Harry Potter and the Big Ugly Spider”; You can declare and construct a String in the same step if you wish. Using this technique, the memory to hold “Jane Jones” is allocated at the same time name2 is created. This is not always possible, however, particularly in the case when a String is to be input by the user. String name2 = new String(“Jane Jones”); //More examples of declaring and assigning a value String restaurant = new String(“Pepe Pizza”); String favPizza = new String(“Extra Cheese”); You may also use the following shortcut to declare and construct Strings: String name2 = “Jane Jones”; String restaurant = “Pepe Pizza”; String favPizza = “Roasted garlic and tomato”; 2 B. References to a String You can assign a new value to a String after it is initialized. Java will find a new place in memory to store the new String contents. String literals (values) can contain any combination of characters such as letters, numbers, punctuation and special characters such as \n and \t. Remember that String values must always be placed in “quotes” just like characters are always placed in ‘apostrophes’. Assuming you already declared food1 and food2, you can enter their values in quotes: food1 = “sushi”; food2 = “waffles”; However, avoid assigning one String to another, for it doesn’t work the way you think. food2 = food1; Remember, the String identifier is a reference to the string data. The identifier points to the place where the String is stored in memory. In the example above, food2 will now point to the part of memory where “sushi” is stored. “waffles” is no longer pointed to by any identifier so consider it gone. Strings are immutable, meaning they cannot be changed. When a series of characters is stored in a block of memory (when a String gets its initial value), that block of memory cannot be changed. If you add onto a String or change it, the computer finds a new place in memory for the changed String. Then, the identifier points to the new place in memory instead of the original place. C. Concatenation and Promotion Here’s a little reminder as to how concatenation with the + operator works: String first_name = new String(“Howdy”); String last_name = new String(“Doody”); String whole_name; whole_name = first_name + “ ” + last_name; Any primitive or object type can be promoted to a String. So Java makes it easy to use the print methods with Strings and other data types. Because of this, you can create complex print statements by concatenating different types. Note the example below. public static void main(String[] args) { String s = "The values of n and x are"; int n = 10; double x = 15.0; System.out.println(s + " " + n + " and " + x); } 3 D. Methods of the String Class There are many useful member methods of the String class. Remember, since a String is an object, you will use dot notation to call its methods. .length() This method returns an integer that indicates the length of the String. The length is how many characters are in the String. This includes spaces. To calculate the length, just count the number of characters. In the following example, what is the value of len? String address1 = new String(“423 Fox Chapel Road”); int len = address1.length(); 1 2 3 4 2 3 4 5 6 7 F o x 8 9 10 11 12 13 14 C H a p e l 15 16 17 18 19 R o a d charAt(int index) The .charAt method returns whatever character is stored in the index you specify in the parameter. You already know that the characters in a String are indexed starting at 0. Let’s say you want to examine the 4th character in a String. You could write: String cityA = “New York”; String cityB = “New London”; if(cityA.charAt(4) == ‘Y’) System.out.println(“The city is New York!”); else System.out.println(“The city is New London.”); Note that .charAt returns a primitive char value, not a String. toUpperCase() and toLowerCase() These methods will provide all the alphabetic characters in a String to upper case or lower case as specified. The function returns the converted String leaving the original unchanged. See below. String mixed = “AbcDEfgHIj”; String fixed; fixed = mixed.toLowerCase(); //The value of fixed is “abcdefghij” fixed = mixed.toUpperCase(); //The value of fixed is “ABCDEFGHIJ” 4 .substring(int firstIndex, int lastIndex) or .substring(int firstIndex) The substring method is overloaded, in that it has more than one meaning. You can either get a piece of a String, starting at a certain index and ending at a different index or you can get the tail-end of a String starting at whatever index and going until the end of the String. Note, if you try to access an index that is out of range (ie. Outside the String), you will get an error. The generic method syntax is as follows: identifier.substring(start, stop); Where start is the index to start at and stop is the index of the character immediately AFTER the last character you want to collect. Here is a more concrete example: address1.substring(0,3); 0 1 2 3 4 2 3 4 5 6 7 F o x 8 9 10 11 12 13 C h a p e l 14 15 16 17 18 R o a d The method would return the sub-string “423”. This sub-string consists of the first three characters in the string (at indices 0, 1 and 2). If you wanted the word “Chapel” to be returned, you would have to call the method: address1.substring(8, 14). The stop parameter is 14 because that is one index after the last character that you want. Questions: What will the following methods return? address1.substring(8); address1.substring(10, 11); address1.substring(14); address1.substring(0, 7); .equals(String other) You cannot use the comparison operator “==” to determine if two Strings are identical. Instead, you must use the .equals method. This is a boolean function that returns true if the String you are testing is equal to the other String. Equality means it is the exact same sequence of characters (same case, spaces and special characters, too). So, “Dog” is equal to “Dog” but “dog” is not equal to “Dog”. Which result will be displayed in this example? snack1 = “cookies”; snack2 = “oreos”; if(snack1.equals(snack2)) System.out.println(“Exactly the same snack.”); else System.out.println(“Not exactly the same snack.”); 5 compareTo(String other) This method tests which string comes after the other in order. Think about the dictionary—words that appear in the front (starting with a, b, or c) are “less than” the words that appear at the end (starting with x, y, z). Thus the word “person” is greater than the word “people.” You may want to think about this as testing alphabetical order, but there is a bit more to it. All uppercase letters are less than all lowercase letters. And all numeric digits are less than all letters. Java puts letters and numbers in order like this: <- Least 0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ Greatest -> abcdefghijklmnopqrstuvwxyz You may think this is a random way to order things, but it is based on the ASCII (American Standard Code for Information Interchange) character set which contains 128 characters in all—this includes punctuation and special characters (like the return key on the keyboard). The ASCII code is a subset of the Unicode character set, which Java uses. Other programming languages like C and C++ just use ASCII but since Java was designed with a broader usage in mind, it uses Unicode. Unicode has 65,536 different characters and is therefore able to represent the characters of many foreign languages. Here’s an example: String firstString = “Leg warmers”; String otherString = “Leg of lamb”; int result = firstString.compareTo(otherString); The method returns a negative integer if the first String is less than the other String. It returns positive if the first String is greater than the other String. Which String is greater? At the fourth index of each String, we find a different character. Which is less, an ‘o’ or a ‘w’? “Leg of lamb” is less, so this returns a negative number. This may seem confusing so here are some additional examples. To compare stringA and stringB, the following would occur. Note, in this example a number in [brackets] indicates the index number. stringA[0] is compared to stringB[0] stringA[1] is compared to stringB[1] stringA[2] is compared to stringB[2] . . . and so on and so forth until a dissimilar character is found. At the time that it finds a dissimilar character, it stops comparing. So “turkey” is less than “turnip” because ‘k’ is less than ‘n’. Also note that the word “turk” is less than “turkey”. The value of each character is determined by the ASCII code (each character is represented by an integer). So, in reality, you are comparing their numerical values. Also recall that all capital letters appear before all lowercase letters in the ASCII code. So, an upper case letter will always be less than a lowercase letter. Therefore, “Peanut” is always less than “peanut”. “Peanut butter” is less than “peanut”. Finally, “peanut butter” will always be greater than “peanut”. Finally, if two strings are identical, such as “peanut” and “peanut”, the .compareTo function will return a 0. 6 It is not important what the exact value of the positive or negative integer is; the significance is that you can use this in a conditional statement in your program. If the two compared Strings are equal, the .compareTo function will return a 0. For example: public static void main (String[] args) { String animal1 = "dinosaur"; String animal2 = "Dinosaur"; int result = animal1.compareTo(animal2); if(result > 0) System.out.println(animal1 +“ is greater than ” +animal2); else System.out.println( animal2 +“ is greater than ” +animal1); } .indexOf(String target) Every String is indexed such that each character has an index number. The index starts at 0, so the first index is 0. Do not confuse this with the length of the string that starts counting at 1. Again, we start with the String object: String address1 = new String(“423 Fox Chapel Road”); 1 2 3 4 2 3 4 5 6 7 F o x 8 9 10 11 12 13 14 C h a p e l 15 16 17 18 19 R o a d This method is used to find the index of a certain substring within a string. It returns an integer. In the example below, the method returns the number 4. int i = address1.indexOf(“Fox”); //Finds the index of the first //occurrence of “Fox” Let’s say that you would like to find the location of a certain character. You can also use the .indexOf method. For example, to find where the first instance of the letter ‘R’ is located in the string, you would write the following code. What index value will this method return? int i = address1.indexOf(“R”); If when using the .indexOf function, the desired substring is not found, a “–1” is returned. -1 is a good choice for the return value because it can never be the index of a string since they are indexed starting at 0. 7 E. String-Returning Methods You will of course have noticed that many string methods produce a result. For instance: int value = myString.length(); invokes a method that counts the number of characters and assigns the result into the desired variable. It is possible for you to program your own methods that act similarly. These are called value-returning methods. The idea is that a method performs an action, but also responds with certain data. The methods that you have written so far have been headed with the keyword “void”, meaning that they perform an action but do not evaluate to any data. Replacing the keyword “void” with a data type allows you to utilize the method in the context of an assignment statement. Since strings are immutable, these types of methods are commonly used to generate new strings based off an existing one. Let’s suppose that you wanted a method that counted the number of spaces in a string. You could write a loop that looks at each character, using .charAt(), and increment an integer variable each time a space was encountered. Here’s how such a method will look in its entirety. static public int countSpaces(String text) // check each character to see if it is a space { int count = 0; for (int i = 0; i < text.length(); i++) { if (text.charAt(count) == " ") { count++; } } return count; } The “return” statement tells Java what value should be filled in for the calling code. The return statement ends the method, so it should always appear at the end of the method. A sample method call might be: System.out.println(countSpaces(“The quick brown fox”)); This invokes the “countSpaces” method, substituting as a parameter the string “The quick brown fox”. The method then processes the loop, counting the spaces. When the method reaches the return statement, the value of “count,” in this case 3, is substituted for the method call. Valuereturning methods are used in the context of the data type that they return. Here is another application that uses the countSpaces method. String phrase1 = “open ses a me”; String phrase2 = “ o p ense a me”; if (phrase1.countSpaces() > phrase2.countSpaces()) System.out.println(“First one has more spaces”); else System.out.println(“Second one has more spaces”); 8 You may also want to think of value-returning methods as providing something like a reverseparameter. Parameters serve as input to the method, and the value returning is the output. There can only be one return value, however, while there can be many parameters. Here is a method that will receive a number and produce a String containing as many underscores as the number. It will put a space between each underscore. static public String toUnderscores(int size) // size determines the number of underscores to produce { String text = “”; for (int i = 0; i < size; i++) { text += “_ ”; } return text; } The method call System.out.println(toUnderscores(5)); will display: “_ _ _ _ _ ” on the console. Try one now for yourself. Write a String-returning method that will produce the reverse of a given String. Thus “reverse” produces “esrever”. public static String reverse(String s) { // design this yourself } Now write a method that, given a string, returns the same string except with any blank spaces removed. Thus “op ens esa me” produces “opensesame”. public static String eliminateSpaces(String s) { // design this yourself } 9 F. Random Number Generators Using a value-returning method, we can now create a way of producing a different word each time our program is run. Using the random number generator is quite simple. Java has some utilities that assist us with this. The Math class contains a Math.random() function that produces a floating point number in the range [0.0 . . 0.999). Let’s suppose, in a simple scenario, that we have five possible choices for a word: “academy,” “mischief,” “rewind,” “blatant,” and “onyx.” The following method will select one of these words at random. public static String getRandomWord() { // selects one of five words at random double dNum; int selection; String value = “”; dNum = Math.random() * 5; // number of possibilities selection = (int) dNum; // type cast if (selection == 0) value = “academy”; else if (selection == 1) value = “mischief”; else if (selection == 2) value = “rewind”; else if (selection == 3) value = “blatant”; else if (selection == 4) value = “onyx”; return value; } Since there are a lot of if / else statements, perhaps you may want to try a “switch” structure. The “switch” structure acts in the same way as a series of if / else when you are evaluating one variable. Note that when evaluating each case, there is a “break” command that indicates when that case is finished. The “break” indicates that flow of control should pass on to whatever follows the switch structure. public static String getRandomWord() { // selects one of five words at random double dNum; int selection; String value = “”; dNum = Math.random() * 5; // number of possibilities selection = (int) dNum; // type cast switch (selection) { case 0: value = “academy”; break; case 1: value = “mischief”; break; case 2: value = “rewind”; break; case 3: value = “blatant”; break; case 4: value = “onyx”; break; } return value; } 10 G. File I/O If you want to get really elaborate, you may want to have Java read a word from a dictionary file, thus making many possibilities. Let’s assume that we have a dictionary file on hand, called “words.txt.” This file contains 26,870 words. The first task for the computer might be to generate a random number from 1 to 26,870. Since each word is on a different line, we can set up a loop that will find the correct word. All we have to do is learn commands to have the computer look in the file. File I/O (I/O stands for input/output) is the process of reading from (input) and writing to (output) files. You have already worked with console I/O when you have written to the screen and collected user input via the keyboard. There are a number of file I/O classes in Java. To simplify them, there are a couple of classes in the “cs” package. In this course, we will use the Infile and Outfile classes. To access them, include the following at the top of your program: import cs.ssa.*; Before getting too nitty gritty with the details, ask yourself these questions: What are the possible uses of file I/O in applications, apart from our use here? Certainly, there are times when you want to save your data to a file, particularly in an application where a person may want to leave off where they were and come back. What are the possible benefits of file I/O for testing and debugging? The notion of data flowing from a file to a program and back again is called a stream. Streams only go one way so you can have a stream of one of the following types: input stream or output stream. Input streams go from a file to your program (into your program) whereas an output stream goes from your program to a file (out of your program). In order to read or write to a file, you must open an appropriate stream to that file. Think of a stream as the pipe, which sends information from your program to a file or vice versa. The files that you will work with should be stored within the same directory as your programs. Your files could be made using Textpad or Notepad. They can have names such as name.txt (with .txt as the extension) or name.dat (with .dat as the extension—stands for data). If you are reading data from a file, then you must create it before trying to use it. If you are writing to a file, then the file will be created by your program and you do not need to set it up before hand. To open a new file for reading, use the following syntax: InFile inputFile = new Infile(“info.txt”); You can open a different file after constructing an inputFile object if needs be. For instance, if you must get the filename from the user, you can do the following: InFile inputFile = new InFile(); System.out.println(“Enter the name of your file ” +“(include file extension): ”); String filename = Keyboard.readString(); inputFile.open (filename); 11 Methods of the InFile class that are useful to know include: int readInt () double readDouble() String readString() String readlnString() int readlnInt() double readlnDouble() void readln () boolean eof(); void close(); reads a single integer (note that multiple integers may be on one line in a file) reads a double reads text—if more than one word is on a line in the file, then only the first word is read reads text, but will read the entire line regardless of spaces reads an int and ignores the rest of the line reads a double and ignores the rest of the line skips to the next line returns true if the end of file is reached closes the file—important for output files! For example, imagine that we are reading from a file that contains your name and age. The following short program will read the data and print it on the screen: import cs.ssa.*; class FileTest { public static void main(String[] args) { String name; int age; Infile inputFile = new Infile(); inputFile.open("myFile.txt"); name = inputFile.readString(); age = inputFile.readInt(); inputFile.close(); System.out.println(“Your name is: ” + name); System.out.println(“Your age is: ” + age); } } As you may guess, printing data to a file will utilize the “OutFile” class. If the file name does not exist before you open it, then it will be created. If it does exist, then its contents will be overwritten. Some of the common methods are: void open () // opens the file void print(int/double/String) // operates on common data types void println(int / double / String) // prints data and goes to // a new line void close(); // closes the file 12 Examine quickly the code below. What do you think it achieves? import cs.ssa.*; import cs1.*; class Mystery { public static void main(String[] args) { String name, password; OutFile outputFile = new OutFile(); outputFile.open("myFile.txt"); System.out.print(“Enter name: ”); name = Keyboard.readString(); System.out.print(“Enter password: ”); password = Keyboard.readString(); outputFile.printLn(name); outputFile.printLn(password); outputFile.close(); } } It is very important that you always close your files after you are done using them. If you do not close an output file, then everything you write to it may be lost when the program terminates. The longer a file is open the greater the chance it has of being corrupted. For example, if your computer crashes before your files are closed, you may lose data. Java protects programmers from making errors, or exceptions, with file I/O by demanding that the programmer take care of possible errors within the code. Errors that occur when reading from and writing to files produce a certain type of exception called an IOException. A function that might cause this type of error is said to throw this exception. Some exceptions you have seen in Java crash your program, for example, reading in the wrong data type. We will learn more about exception handling at a later time. For now, make sure you are aware that many things can go wrong with file I/O: a file may be corrupted on the disk, it may not exist, or perhaps if the file is lengthy, another process may change it at the same time that your program is trying to access it. To finish, here is a method that will get a random word from an input file: public static String getWordFromFile() { // selects a word from a dictionary file double dNum; int selection; String value = “”; dNum = Math.random() * 5; // number of possibilities selection = (int) dNum; // type cast InFile inputFile = new InFile(“words.txt”); for (int count = 0; count < selection; count++) value = inputFile.readString(); inputFile.close(); return value; } 13 String Exercises #1: Given the following string declaration: String location = new String (“Taj Mahal”); What do the following function calls return? 1. location.length() returns: _______ 2. location.charAt(2) returns: ________ 3. location.indexOf(“Mahal”) returns: ________ 4. location.equals(“India”) returns: ________ 5. location.compareTo(“taj mahal”) returns: ________ 6. location.toUpperCase() returns: ________ What is the final value of the text contained in location after all of the above calls are made? Try the following exercise in comparing animals. Assume you have the following String objects: String String String String animal1 animal2 animal3 animal4 = = = = “dog”; “cat”; “Dogfish”; “catfish”; What would the following comparisons evaluate to? Write P for positive or N for negative or 0 for zero next to each. ____ animal1.compareTo(animal2); ____ animal3.compareTo(animal1); ____ animal2.compareTo(“cat”); ____ animal4.compareTo(animal2); 14 String Exercises #2 1. What will be output when the program segment executes? String s1 = “palindrome”; String s2 = new String(“radar”); String s3 = new String (“radar”); if (s1.equals(s2)) System.out.print(“equals ”); else System.out.print(“not equals ”); if (s2.equals(s3)) System.out.print(“equals”); else System.out.print(“not equals”); a. b. c. d. e. equals equals equals not equals not equals equals not equals not equals Some error message. 2. What will be output when the program segment executes? String s1 = “palindrome”; String s2 = new String(“palindrome”); String s3 = s1; if (s1 == s2) System.out.print(“equals ”); else System.out.print(“not equals ”); if (s1 == s3) System.out.print(“equals”); else System.out.print(“not equals”); a. b. c. d. e. equals equals equals not equals not equals equals not equals not equals Some error message. 15 3. What string value is returned by the call question(“aardvark”)? public static String question(String s) { // precondition: s is not null String t = “”; for (int k = 0; k < s.length(); k++) t += s.charAt(k); return t; } a. aardvark b. varkaard c. kkkkkkkk d. kravdraa e. None of the above. 4 - 10. Given the following expressions: String s1 = new String(“problem solving”); String s2 = new String(“java”); String s3 = new String (“great work”); a. s1.length() returns _______ b. s3.length() returns _______ c. s1. charAt(7) returns _______ d. s2. charAt(7) returns _______ e. s3.compareTo(s2) returns _________________ f. s2.compareTo(s3) returns _________________ g. s3.indexOf (“work”) returns _________________ 16 AP Computer Science Project #3: Morphology Specifications: Most spoken languages have rules by which the nouns and/or the verbs change their form, usually according to criteria such as person, gender, time, voice, etc. For instance, the English language adds the suffix “-ed” to some verbs to indicate the past tense, and adds the helping verb “has” to indicate the perfect tense. Your task is to aid foreign students of elementary English by writing a program that will take any noun and verb and produce proper present, past and future tense sentence forms of that verb. You program should handle regular nouns that add –s and those that add –es. It should handle past tense / participles that add –ed. It should also include a small database of at least 10 irregular nouns (e.g. goose geese) and 10 irregular verbs (e.g. see saw, seen). A sample run-time format might be: Welcome to the morphology program. . . . Please enter a subject (noun) in its singular form (‘quit’ ends): dog Please enter a verb in its base form (infinitive without “to”): walk dog walks; dog walked; dog has walked; dogs walk; dogs walked; dogs have walked Please enter a subject (noun) in its singular form (‘quit’ ends): watch Please enter a verb in its base form (infinitive without “to”): run watch runs; watch ran; watch has run; watches run; watches ran; watches have run Please enter a subject (noun) in its singular form (‘quit’ ends): fish Please enter a verb in its base form (infinitive without “to”): see fish sees; fish saw; fish has seen; fish see; fish saw; fish have seen This is only a sample—you may change your format as it suits you, so long as it is user-friendly. Prompt the user with clear instructions about the input format, etc. Design: Your program may use only one class, the driver program. It must be properly subdivided into methods and it must appropriately use variables at the parameter level, local level, and / or class level. Implementation: You must include at least one String-returning method to handle regular word changes. A suggestion might be: static public String getRegularPastTense(String verb) You also must use file I/O. I suggest that you make two data files, one of which contains irregular noun forms and one that contains irregular verb forms. The first line of the file may contain an integer that indicates how many lines of data that the file contains. For instance, you may create a file called “nouns.txt” as follows: 17 5 fish fish mouse mice sheep sheep ox oxen octopus octopi Then you may write a method called “checkIrregularNoun”: static public String checkIrregularNoun(String noun) // returns “null” for a non-irregular noun; // otherwise, returns the irregular form { String value = null; InFile myFile = new InFile(“nouns.txt”); int numLines = myFile.readInt(); for (int x=0; x < numLines; x++) { String current = myFile.readString(); if (current.equals(noun)) value = myFile.readString(); // assign the irregular form else myFile.readlnString(); // skip remainder of the line } myFile.close(); return value; } Testing: Test your program with a variety of inputs. When you do so, keep track of your testing. Handle some common exceptions. In your concluding reflections, address: - List some specific ways that the program could be expanded to handle more “natural language” features. - For a foreign language (you choose which one), list at least three of the morphology rules that a similar program should handle. 18 AP Computer Science Project #2: Mad-Libs Specifications: Your computer will choose a short nursery rhyme and allow the user to fill in blanks for at least four words in Mad-Libs style. Consider the example of “Humpty-dumpty.” Your program will establish a string containing the rhyme, marking out the substitutions in between the asterisk (‘*’) characters as such: rhyme = “Humpty dumpty sat on a *singular noun*\nHumpty dumpty had a great *singular noun*\nAll the king’s *plural animal* and . . ..”; The program will scan for areas between the * marks and prompt the user to fill in those blanks. Then the program will display the resulting nursery rhyme. Your program should scan for line breaks and place your text accordingly. The computer should select the nursery rhyme at random from a file. The output should be displayed on a window, showing line breaks in the appropriate places. Design: Your program may use only one class, the driver program, although you may also use an extended window class to help display the final output. Your program must be properly subdivided into methods and it must appropriately use variables at the parameter level, local level, and / or class level. 19 Implementation: You must include at least one String-returning method. A suggestion might be: static public String replace1Blank(String entireRhyme) You also must use file I/O. The first line of the data file may contain an integer that indicates how many lines of data that the file contains. For instance, you may create a file called “rhymes.txt” as follows: 3 Humpty dumpty sat on a *singular noun*\nHumpty dumpty had a great . . . Twinkle twinkle little *singular noun*\nHow I wonder . . . When I was going to Saint *saint’s name*\nI met a man with *number* wives Thus reading the file can be done as follows: static public String getRhyme() // chooses a rhyme at random from the file { String value = null; InFile myFile = new InFile(“nouns.txt”); int numLines = myFile.readInt(); int rhymeNumber = (int)(numLines * Math.random()); for (int x=0; x < rhymeNumber; x++) { value = myFile.readlnString(); } myFile.close(); return value; } // get random # Note that the line break symbols ‘\n’ will literally be stored as a backslash and an ‘n’. You will need to scan your string and replace those two characters with the single ‘\n’ in your program. Testing: Test your program with a variety of inputs. When you do so, keep track of your testing. Handle some common exceptions. In your concluding reflections, address: - Select one of your rhymes. How many characters are stored in memory when it is read into a string variable (include white spaces)? For this rhyme, how many characters are being stored by the program in memory at worst? This will take some counting. Consider that strings are passed as reference parameters. 20