INFORMATION & COMPUTER SCIENCE DEPARTMENT, KFUPM
ICS102, SECTIONS 52, 54 & 55
(001 Semester)
INTRODUCTION TO COMPUTING
LAB #12 String and File Processing
Instructor: Bashir M. Ghandi
To
gain experience with:
String processing is one of the most common applications of computers. Java recognizes this fact, and therefore provides a String class that has many methods that can be used to process or manipulate strings
The following table shows some of the methods of the String class. For a complete list of methods, check the JDK documentation:
method |
description |
myString = "examples" |
int |
returns the number of characters in this String. |
int i = myString.length(); // i = 8 |
String |
returns a new String, representing the Upper-case equivalent of this String. |
String upper=myString.toUpperCase(); // upper == "EXAMPLES" |
String |
returns a new String, representing the lower-case equivalent of this String. |
String lower=myString.toLowerCase(); // lower == "examples" |
boolean Boolean (String s) |
returns true if the argument has the same length, and same characters as this String. the second version ignores the case. |
if (myString.equals("EXAMPLES") ) // false |
int int (String s) |
returns positive number, 0, or negative number if this String is lexicographically greater than, equal to or less than the argument respectively. |
if (myString.compareTo("Yes")>0 ) // true |
char |
returns the char in this String, at the index position passed to the method. Note: Index positions always start at 0 |
char c = myString.charAt(6); // c = ‘e’ |
int int //also overloaded to accept String parameter |
finds the first / last occurrence of a character or substring within this string, and returns the index. If the substring or char is not found -1 is returned. |
int i = myString.indexOf("amp"); // i == 2 if (myString.indexOf("Y") > 0) // returns -1 and is false |
String (int from) String (int from, int to) |
returns a part of the original String starting from the fromIndex to the end of the string or up to but not including toIndex if one is given. |
String s=myString.substring(5) // returns "ample" and "les" String s=myString.substring(2, 7); // returns "les" |
String |
returns a String, produced by removing any whitespace characters from the beginning and end of this string. |
String s = “ 125 “; s = s.trim(); //returns “124” |
static String (any primitive type) |
is a static method, that is overloaded for all the primitive data types, and returns a String representation of the argument. |
String s = String.valueOf(3.14F); // numbers to Strings "3.14" |
String concat(String s) //equivalent to + symbol |
append the argument string at the end of this string. |
String s = myString.concat(“ below”) // returns “examples below” |
Note:
Java treats String as a special class in the
sense that it has features that other Java classes do not have. Some of these features are:
·
A
String object can be created without using the new keyword. For example,
String name = new String(“Bashir Ghandi”);
can
be written simply as:
String name = “Bashir Ghandi”;
·
The
plus operator ‘+’ can be used to append strings. Example:
String fullName = “Bashir”+” Ghandi”;
The readLine() method of the BufferedReader
object reads an entire line at a time.
However, there are many situations where we would like to break the line
into individual words (called tokens) for further processing. For example, if we read the string:
“996502 10 15 20”
we would like to break the string into the
following:
“996502” “10” “15” “20”
so that we can call the appropriate parser method to
convert the tokens to numbers.
To achieve this, Java provides the class, StringTokenizer
in the java.util package.
To use this class, we need to first create its
object using of the constructors.
The following table shows the constructors and the
main methods of the StringTokenizer class.
constructors and methods |
description |
|
Assumes white space as delimiters (“ \t\n\r”) |
|
Uses the characters in the second argument as delimiters. |
|
If the third argument is true, include the delimiters as part of
the tokens. |
|
Returns the next token from this tokenizer’s string |
|
Tests if there are more tokens from this tokenizer's string |
|
Returns the number of tokens remaining in this tokenizer’s
string. |
Example 1: The following program outputs the tokens in a
string provided by the user.
import java.io.*; import java.util.*; class TokenizerExample { static BufferedReader stdin = new
BufferedReader(new InputStreamReader(System.in)); public static void main(String[] args)
throws IOException { int numberOfTokens=0; System.out.println("Input a
string "); String line = stdin.readLine(); StringTokenizer data = new StringTokenizer(line); numberOfTokens = data.countTokens(); System.out.println("Number of tokens = " +
numberOfTokens + "\n"); for (int i=0; i < numberOfTokens; i++) { System.out.println(data.nextToken()); } } } |
Example 2: The
following example shows how StringTokenizer may be used to process
numbers. It reads a requence of numbers
typed on one line, separated by comma or spaces and prints the average and
those values below average.
import java.io.*; import java.io.*; import java.util.*; class TokenizerExample2
{
static BufferedReader stdin = new BufferedReader(new
InputStreamReader(System.in)); public
static void main(String[] args) throws IOException { int
numberOfTokens=0; int[] numberArray; int total=0; double average; System.out.print("Input
list of integers separated by commas or space: "); StringTokenizer data = new
StringTokenizer(stdin.readLine()," ,"); numberOfTokens = data.countTokens(); System.out.println("Number of tokens = " +
numberOfTokens + "\n"); numberArray = new int[numberOfTokens]; for (int i=0; i < numberOfTokens; i++) {
numberArray[i] = Integer.parseInt(data.nextToken());
System.out.println(numberArray[i]); total =
total+numberArray[i]; } average = (double)total/numberOfTokens; System.out.println("---------\n" + total + " (average = " +average+ ")"); System.out.println("\nValues below average
are:"); for (int i=0; i<numberOfTokens; i++) if (numberArray[i] < average) System.out.println(numberArray[i]); } } |
The applications we have written so far read input
from the keyboard and displayed output on the screen. These methods of input/output are inadequate for programs that
creates large amount of data. In this
section we examine how input may be read from a file and how output may be
written to a file using a number of classes in the java.io package.
3.1 Reading input from a
file.
To read input from a file, we make use of the
following classes:
FileInputStream:
This is used to create a stream source (an object from which streams can
be read. For example:
FileInputStream inFile
= new FileInputStream(“mydata.txt”);
This opens the file “mydata.dat” for reading and
associates it with the inFile object.
If the file does not exists, a FileNotFoundException
is thrown.
If the file is not in the current folder, the full
path of the file must be specified.
When specifying the full path of the file, it is
better to use forward slash. Example: “C:/workarea/mydata.txt”
The back slash can also be used, but double slash
must be used for each slash. Example:“C:\\workarea\\mydata.txt”
However, if the file name is being read from the
user using a string variable, then only single slash is used.
InputSreamReader:
This (as explained before) is used to create an object which can be used
to read one character at a time from a stream source. Example:
InputStreamReader reader = new InputStreamReader(
new
FileInputStream(“mydata.txt”));
BufferedReader:
This (as explained before) is used to create an object that can be used
to read one line at a time from a stream reader (using its readLine()
method). Example:
BufferedReader in = new BufferedReader(
new
InputStreamReader(
new FileInputStream(“mydata.txt”)));
FileReader: This is a used to directly create a stream reader
object from a file, effectively taking over the work of FileInputStream and
InputStreamReader. Thus, the
declaration of the BufferedReader object obove can be simplified as follows:
BufferedReader in = new
BufferedReader(new FileReader(“mydata.txt”));
Notes:
·
These
classes throws IOException and/or FileNotFoundException if an
error occurs. Thus, we must either use
try and catch to handle the possible exceptions or throw such exceptions. FileNotFoundException is in fact a
subclass of IOException, thus, unless there is a need to specifically
handle it, handling IOException can suffice for it.
·
It
is a good practice to always close files using the close() method after
the reading (or writing) is completed.
If fact, sometimes it is necessary to do so, for example, when opening a
file for input after it has been opened for output and vice-versa.
·
The
BufferedReader object associated with a FileInputStream or FileReader
has a method, ready(), that can be used to test if the file pointer has
reach the end of the file. This is very
useful when reading all the lines in a file.
Example 3: The following example reads the content of a file,
myinput.txt, and display it on the screen.
import java.io.*; public class
DisplayTextFile { public static void main(String[] args)
{
String line;
int count = 0;
try { BufferedReader in = new BufferedReader(new FileReader("myinput.txt")); while
(in.ready()) {
line = in.readLine();
System.out.println(line);
count++; } System.out.println("\n"+count + " lines have been read from myinput.txt" );
in.close();
}
catch(FileNotFoundException e) { System.out.println("Sorry,
file myinput.tx was not found");
}
catch(IOException e) { System.out.println("Error reading from file, input.txt");
} } } |
Example 4: The
following example reads integers from the file, integerdata.txt, and
prints the sum, average and list of values below average.
import java.io.*; import
java.util.*; class
ProcessFileOfInteger {
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new
FileReader("integerdata.txt"));
int numberOfTokens = 0, number, count=0, sum=0;
StringTokenizer dataLine;
String line;
while (in.ready()) {
line = in.readLine();
if (line != null) {
dataLine = new StringTokenizer(line);
numberOfTokens = dataLine.countTokens();
for (int i=0; i < numberOfTokens; i++) {
number = Integer.parseInt(dataLine.nextToken());
sum+=number;
count++;
System.out.print(number + " ");
}
System.out.println();
} }
System.out.println("----------------\nCount of numbers:
"+count);
System.out.println("Sum of numbers: "+sum);
System.out.println("Average of numbers: "+sum/(double)count);
in.close();
} } |
3.2 Writing to a file:
To write output to a file, we make use of the
following classes:
FileOutputStream:
This is used to create a stream target (an object into which streams are
written). It has two constructors that
are used as follows:
FileOutputStream
outFile = new FileOutputStream(“result.txt”);
or
FileOutputStream
outFile = new FileOutputStream(“result.txt”, true);
This opens the file “result.txt” for writing. If the file exists, its content is deleted
so that the new output will effectively overwrite the old content of the
file.
If the second parameter of the second constructor is
true, the file is opened for appending.
i.e. the content of the file is NOT deleted, rather any new data is
written at the end of the file.
In either case, if the file does not exist, it is
created. If it cannot be created for
some reason, or if it exist but cannot be opened for some reason, a FileNotFoundException
is thrown.
OutputStreamWriter:
This is used to create an object that can be used to write one character
at a time to a stream target object.
Example:
OutputStreamWriter writer = new OutputStreamWriter(
new FileOutputStream(“result.txt”));
PrintWriter: This is
used to create an object that can be used to print output to an output
writer. The object has print and
println methods that are overloaded for all data types. Example:
PrintWriter out = new
PrintWriter(
new OutputStreamWriter(
new FileOutputStream(“result.txt”)));
FileWriter: This is
used to create stream writer directly to a file. Thus, the PrintWriter object creation above can be simplified as:
PrintWriter out = new
PrintWriter(new FileWriter(“result.txt”));
or
PrintWriter out = new
PrintWriter(new FileWriter(“result.txt”, true));
Example 5: The following example read data from the file storylines.txt
and append it to the file story.txt.
import
java.io.*; public
class AppendText { public static void main(String[] args) { int count = 0; String line; try { BufferedReader in = new BufferedReader(new
FileReader("storylines.txt")); PrintWriter out = new PrintWriter(new FileWriter("story.txt",
true)); while (in.ready()) { line = in.readLine(); if (line != null) {
out.println(line);
count++;
} } System.out.println(count+" lines
have been appended to story.txt" ); in.close(); out.close(); } catch(FileNotFoundException e) { System.out.println("Sorry, one
or both files could not be found"); } catch(IOException e) { System.out.println("Error
handling files"); } } } |
Example 6: The
following example reads ID numbers, names and grades of students in three exams
contained in a file, examdata.txt
and computes the average of each
student. It prints the output on both
the screen and the file, result.txt.
import
java.io.*; import
java.util.*; class
ProcessExamScore { public static void main(String[] args)
throws IOException { BufferedReader in = new
BufferedReader(new FileReader("examdata.txt")); PrintWriter out = new
PrintWriter(new FileWriter("result.txt")); String line, name, iDNumber; double exam1, exam2, exam3, average; StringTokenizer dataLine; line = in.readLine(); while (line != null) {
dataLine = new
StringTokenizer(line, "|");
iDNumber =
dataLine.nextToken();
name = dataLine.nextToken();
exam1 =
Double.parseDouble(dataLine.nextToken().trim());
exam2 =
Double.parseDouble(dataLine.nextToken().trim());
exam3 =
Double.parseDouble(dataLine.nextToken().trim());
average = (exam1 + exam2 +
exam3)/3; System.out.println(iDNumber+"\t"+name+"\t"+exam1+"\t"+exam2+"\t"+exam3+"\t"+average); out.println(iDNumber+"\t"+name+"\t"+exam1+"\t"+exam2+"\t"+exam3+"\t"+average); line = in.readLine();
}
in.close();
out.close(); } } |
Note: If the delimiter of a StringTokenizer does not
include space, then the trim() method must be applied to the tokens to
remove spaces before passing them to a parser methods. Failure to do this will make the parser
methods to throw NumberFormatException.
1.
Modify
example 4 (ProcessFileOfInteger.java) so that it prints the
output both on the screen and in a file, result2.txt
2.
Modify example 6
(ProcessExamScore) so that it also prints the average of each quiz on both the
screen and in the file, result.txt.
3.
Midify Example 1
(DisplayTextFile) so that it also print on the screen, the number of words, the
longest word, the shortest word and the average length of a word in the
file. You may add more words in the
file.
1. Write an application, FrequencyTable, that reads the
data in integerdata.txt
file and prints a frequency table both on the
screen and in the file frequency.txt as shown below: