1
CMPS 12M
Introduction to Data Structures Lab
Lab Assignment 3
The purpose of this lab assignment is to introduce the C programming language, including standard input-output
functions, command line arguments, File IO, and compilation with Makefiles.
Introduction to C
If you are not already familiar with C (or even if you are) it is recommended that you purchase a good C reference
such as C for Java Programmers: a Primer by Charlie McDowell (Lulu.com 2007). The C programming
language is, in a certain sense, the grandparent of Java (C++ being its parent). Java is known as an Object Oriented
Programming (OOP) language, which means that data structures and the procedures which operate on them are
grouped together into one language construct, namely the class. Common behavior amongst classes is specified
explicitly through the mechanism of inheritance. The C programming language on the other hand does not
directly support OOP, although it can be implemented with some effort. C is known as a procedural programming
language, which means that data structures and functions (i.e. procedures) are separate language constructs. There
are no classes, no objects, and no inheritance. New data types in C are created using the typedef and struct
constructs, which will be illustrated in future lab assignments. There is however much common syntax between
Java and C. Many control structures such as loops (while, do-while, for), and branching (if, if-else, switch) are
virtually identical in the two languages. One major difference is in the way program input and output is handled,
both to and from standard IO devices (keyboard and terminal window), and to and from files. The following is
an example of a "Hello World!" program in C.
Example
/*
* hello.c
* Prints "Hello World!" to stdout
*/
#include <stdio.h>
int main(){
printf("Hello World!\n");
return 0;
}
Comments in C are specified by bracketing them between the strings /* and */, and may span several lines. For
instance /* comment */ or
/* comment
comment */
or
/*
* comment
* comment
*/
are all acceptable. With the right compiler flags, Java/C++ style comments are also acceptable.
// comment
// comment
2
You may use any style you like, but throughout this document we will use the older C style /*comments*/.
Any line beginning with # is known as a preprocessor directive. The preprocessor performs the first phase of
compilation wherein these directives, which are literal text substitutions, are performed, making the program
ready for later stages of compilation. The line #include<stdio.h> inserts the standard library header file
stdio.h, which specifies functions for performing standard input-output operations. Notice that preprocessor
commands in C do not end in a semicolon. One can also specify constant macros using the #define preprocessor
directive as follows.
.
1 CMPS 12M Introduction to Data Structures Lab La.docx
1. 1
CMPS 12M
Introduction to Data Structures Lab
Lab Assignment 3
The purpose of this lab assignment is to introduce the C
programming language, including standard input-output
functions, command line arguments, File IO, and compilation
with Makefiles.
Introduction to C
If you are not already familiar with C (or even if you are) it is
recommended that you purchase a good C reference
such as C for Java Programmers: a Primer by Charlie McDowell
(Lulu.com 2007). The C programming
language is, in a certain sense, the grandparent of Java (C++
being its parent). Java is known as an Object Oriented
Programming (OOP) language, which means that data structures
and the procedures which operate on them are
grouped together into one language construct, namely the class.
Common behavior amongst classes is specified
2. explicitly through the mechanism of inheritance. The C
programming language on the other hand does not
directly support OOP, although it can be implemented with
some effort. C is known as a procedural programming
language, which means that data structures and functions (i.e.
procedures) are separate language constructs. There
are no classes, no objects, and no inheritance. New data types
in C are created using the typedef and struct
constructs, which will be illustrated in future lab assignments.
There is however much common syntax between
Java and C. Many control structures such as loops (while, do-
while, for), and branching (if, if-else, switch) are
virtually identical in the two languages. One major difference
is in the way program input and output is handled,
both to and from standard IO devices (keyboard and terminal
window), and to and from files. The following is
an example of a "Hello World!" program in C.
Example
/*
* hello.c
* Prints "Hello World!" to stdout
3. */
#include <stdio.h>
int main(){
printf("Hello World!n");
return 0;
}
Comments in C are specified by bracketing them between the
strings /* and */, and may span several lines. For
instance /* comment */ or
/* comment
comment */
or
/*
* comment
* comment
*/
4. are all acceptable. With the right compiler flags, Java/C++
style comments are also acceptable.
// comment
// comment
2
You may use any style you like, but throughout this document
we will use the older C style /*comments*/.
Any line beginning with # is known as a preprocessor directive.
The preprocessor performs the first phase of
compilation wherein these directives, which are literal text
substitutions, are performed, making the program
ready for later stages of compilation. The line
#include<stdio.h> inserts the standard library header file
stdio.h, which specifies functions for performing standard
input-output operations. Notice that preprocessor
commands in C do not end in a semicolon. One can also specify
constant macros using the #define preprocessor
directive as follows.
/*
5. * hello.c
* Prints "Hello World!" to stdout
*/
#include <stdio.h>
#define HELLO_STRING "Hello World!n"
int main(){
printf(HELLO_STRING);
return 0;
}
Every occurrence of the constant macro HELLO_STRING is
replaced by the string literal "Hello World!n".
Although you can name a C program anything you want, each C
program must contain exactly one function called
main(). As in Java, function main() is the entry point for
program execution. Typically main() will call other
functions, which may in turn call other functions. Function
definitions in C look very much like they do in Java.
returnType functionName(dataType variableName, dataType
variableName,. . .){
6. /* declarations of local variables */
/* executable statements */
/* return statement (provided return-type is not void) */
}
Recall that Java allows variable declarations to be interspersed
among executable statements. Whether this is
allowable in C depends on which version of the language you
are using. In particular the ANSI standard requires
that local variables be declared at the beginning of the function
body and before any other statements. The version
of the C language we will use (C99) is not so strict on this
point, but I recommend that students adhere to the
earlier standard (even in Java programs), since it helps organize
one’s thinking about program variables.
The function printf() prints formatted text to stdout. It’s first
argument is known as a format string and
consists of two types of items. The first type is made up of
characters that will be printed to the screen. The
second type contains format commands that define the way the
remaining arguments are displayed. A format
command begins with a percent sign and is followed by the
7. format code. There must be exactly the same number
of format commands as there are remaining arguments, and the
format commands are matched with the remaining
arguments in order. For example
printf("There are %d days in %sn", 30, "April");
prints the text "There are 30 days in April". Some common
format commands are:
%c character
%d signed decimal integer
%f decimal floating point number
%s string (same as char array)
%e scientific notation
3
%% prints a percent sign
See a good reference on C for other format commands. (Note
java contains a method System.out.printf
that behaves exactly like printf in C. There is a lengthy
8. discussion of this function in the documentation for
java.io.PrintStream.)
Observe that the above main() function has return type int. A
return value of 0 indicates to the caller (i.e. the
operating system) that execution was nominal and without
errors. Actually the proper exit values for main are
system dependent. For the sake of portability one should use
the exit codes EXIT_SUCCESS and EXIT_FAILURE
which are predefined constants found in the library header file
stdlib.h. The exit code allows for the status of
the main program to be tested at the level of the operating
system. (In Unix the exit code will be stored in the
shell variable $status.)
/*
* hello.c
* Prints "Hello World!" to stdout
*/
#include <stdio.h>
#include <stdlib.h>
#define HELLO_STRING "Hello World!n"
9. int main(){
printf(HELLO_STRING);
return EXIT_SUCCESS;
}
Compiling a C program
A C program may be comprised of any number of source files.
Each source file name must end with the .c
extension. There are four components to the compilation
process:
Preprocessor→ Compiler→ Assembler→ Linker
The preprocessor performs only pure text substitutions, i.e.
expanding symbolic constants in macro definitions
and inserting library header files. The compiler creates
assembly language code corresponding to the instructions
in the source file. The assembler translates the assembly code
into native machine readable binary code,
sometimes called an object file (not to be confused with Object
in Java). One object file is created for each source
file. Each object file has the same name as the corresponding
10. source file with the .c extension replaced by .o.
The linker searches specified libraries for functions that the
program uses (such as printf() above) and
combines pre-compiled object code for those functions with the
program's object code. The finished product is a
complete executable binary file. Most Unix systems provide
several C compilers. We will use the gcc compiler
exclusively in this course. To create the object file hello.o do
% gcc –c –std=c99 –Wall hello.c
(Remember % here stands for the Unix prompt.) The –c option
means compile only (i.e. do not link), -std=c99
means use the C99 language standard, and –Wall means print all
warnings. To link hello.o to the standard
library functions in stdio.h and stdlib.h do
% gcc –o hello hello.o
The –o option specifies hello as the name of the executable
binary file to be created. If this option is left out
the executable will be named a.out. To run the program, type
its name at the command prompt.
11. 4
% hello
Hello World!
The whole four step process can be automated by doing one
command
%gcc –std=c99 –Wall –o hello hello.c
which automatically deletes the object file hello.o after linking.
If the program resides in just one source file,
this may be the simplest way to compile. If the program is
spread throughout several source files (as will often
be the case in this course) a Makefile should be used to
automate compilation. Here is a simple Makefile for
hello.c.
# Makefile for hello.c
hello : hello.o
gcc -o hello hello.o
12. hello.o : hello.c
gcc -c –std=c99 -Wall hello.c
clean :
rm -f hello hello.o
Here is an equivalent Makefile that uses macros.
# Makefile for hello.c with macros
FLAGS = -std=c99 -Wall
SOURCES = hello.c
OBJECTS = hello.o
EXEBIN = hello
all: $(EXEBIN)
$(EXEBIN) : $(OBJECTS)
gcc -o $(EXEBIN) $(OBJECTS)
$(OBJECTS) : $(SOURCES)
gcc -c $(FLAGS) $(SOURCES)
13. clean :
rm -f $(EXEBIN) $(OBJECTS)
See the examples page for a fancy hello world program in C that
prints out operating system environment
variables.
Command Line Arguments
The main function in a C program may have two possible
prototypes:
int main();
int main(int argc, char* argv[]);
5
The second prototype is used for C programs that use command
line arguments. The first argument argc specifies
the number of string arguments on the command line (including
the program name.) The second argument is an
array containing the string arguments themselves. (Note that in
14. C a string is just a char array and is specified by
the type char*.) The int parameter argc is necessary since
arrays in C do not know their own lengths, unlike in
Java. The following program behaves similarly to its namesake
in lab2.
/*
* CommandLineArguments.c
*/
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[]){
int i;
printf("argc = %dn", argc);
for(i = 0; i<argc; i++ ){
printf("%sn", argv[i]);
}
return EXIT_SUCCESS;
}
15. Compile this program and run it with several different sets of
arguments. Notice that there is one important
difference between this program and the Java version from lab2.
If the executable jar file is called CLA for brevity,
then the Java version gives
% CLA x y z
args.length = 3
x
y
z
whereas the C version above gives
% CLA x y z
argc = 4
CLA
x
y
z
16. We see that in C, argv[0] is the name of the command that
invoked the program, i.e. CLA. In java the array of
strings holding the command line arguments does not include
the name of the program.
Console Input and Output
The function printf() discussed above sends its output to stdout,
which is the data stream normally associated
with the terminal window. The library stdio.h also includes a
function called scanf(), which is a general
purpose input routine that reads the stream stdin (the data
stream normally associated with the keyboard) and
stores the information in the variables pointed to by its
argument list. It can read all the built-in data types and
automatically convert them into the proper internal format. The
first argument to scanf() is a format string
consisting of three types of characters: format specifiers,
whitespace characters, and non-whitespace characters.
The format specifiers are preceded by a % sign and tell scanf()
what type of data is to be read. For example %s
reads a string while %d reads an integer. Some common format
codes are:
17. 6
%c character
%d signed decimal integer
%f decimal floating point
%s string (i.e. char array)
See a good C reference for other scanf() codes, which are
similar but not identical to printf()'s codes. The
format string is read left to right and the format codes are
matched, in order, with the remaining arguments that
comprise the argument list of scanf(). A whitespace character
(i.e. space, newline, or tab) in the format string
causes scanf() to skip over one or more whitespace characters in
the input stream. In other words, one
whitespace character in the format string will cause scanf() to
read, but not to store, any number (including
zero) of whitespace characters up to the first non-whitespace
character. A non-whitespace character in the format
string causes scanf() to read and discard a single matching
character in the input stream. For example, the
control string " %d, %s" causes scanf() to first skip any number
of leading whitespace characters, then read
18. one integer, then read and discard one comma, then skip any
number of whitespace characters, then read one
string. If the specified input is not found, scanf() will
terminate.
All the variables receiving values through scanf() must be
passed by reference, i.e. the address of each variable
receiving a value must be placed in the argument list. We use
the "address-of" operator & to specify the address
of a variable. For instance the following program asks the user
for three integers, then echoes them back to the
screen.
#include<stdio.h>
#include<stdlib.h>
int main(){
int x, y, z;
printf("Enter three integers separated by commas, then press
return: ");
scanf(" %d, %d, %d", &x, &y, &z);
printf("The integers entered were %d, %d, %dn", x, y, z);
return EXIT_SUCCESS;
19. }
A sample run of this program looks like
% a.out
Enter three integers separated by commas, then press return: 12,
-7, 13
The integers entered were 12, -7, 13
Running again, but this time leaving out the separating commas
in the input line gives
% a.out
Enter three integers separated by commas, then press return: 12
-7 13
The integers entered were 12, 4, -4261060
Since the comma separating the first and second integers was
left out scanf() read the first integer, then expected
to read and discard a comma but failed to do so, then just
returned without reading anything else. The values
printed for variables y and z are random values stored in
uninitialized memory and will no doubt be different
20. when you run the program. Thus we see that scanf() is intended
for reading formatted input. This is what the
"f" in its name stands for. The scanf() function returns a
number equal to the number of fields that were
successfully assigned. That number can be tested and used
within the program, as the following example
illustrates.
#include<stdio.h>
#include<stdlib.h>
7
int main(){
int n, i; int x[3];
printf("Enter three integers separated by spaces, then press
return: ");
n = scanf(" %d %d %d", &x[0], &x[1], &x[2]);
printf("%d numbers were successfully read: ", n);
for(i=0; i<n; i++) printf("%d ", x[i]);
printf("n");
return EXIT_SUCCESS;
21. }
Some sample runs of this program follow:
% a.out
Enter three integers separated by spaces, then press return: 1 2
G
2 numbers were successfully read: 1 2
% a.out
Enter three integers separated by spaces, then press return:
monkey at the keyboard!
0 numbers were successfully read:
If an error occurs before the first field is assigned, then scanf()
returns the pre-defined constant EOF. Reading
the end-of-file character is considered such an error. You can
place an end-of-file character into the input stream
by typing control-D. The value of EOF is always a negative
integer and is defined in the header file stdlib.h.
Evidently the value of EOF on my system is -1, as the following
test run illustrates.
% a.out
22. Enter three integers separated by spaces, then press return: ^D
-1 numbers were successfully read:
%
File Input and Output
The library header file stdio.h defines many functions for doing
various types of input and output. See any
good C reference for a listing of these functions (e.g. getc(),
getchar(), gets(), putc(), putchar(), and
puts() are commonly used by C programmers). Most of these
functions have both a console version and a file
version. The functions fprintf() and fscanf() are the file
equivalents of printf() and scanf()
respectively. They operate exactly as do printf() and scanf()
except that they have an additional first
argument that specifies a file to write to or read from. This file
"handle" as it is sometimes called, is a variable
of type FILE*, which is defined in stdio.h. Files are opened and
closed using the fopen() and fclose()
functions. The following program illustrates the use of
functions fopen(), fclose(), fscanf(), and
fprintf().
23. /*
* FileIO.c
* Reads input file and prints each word on a separate line of
* the output file.
*/
#include<stdio.h>
#include<stdlib.h>
int main(int argc, char* argv[]){
FILE* in; /* file handle for input */
FILE* out; /* file handle for output */
char word[256]; /* char array to store words from input file */
/* check command line for correct number of arguments */
if( argc != 3 ){
printf("Usage: %s <input file> <output file>n", argv[0]);
8
24. exit(EXIT_FAILURE);
}
/* open input file for reading */
in = fopen(argv[1], "r");
if( in==NULL ){
printf("Unable to read from file %sn", argv[1]);
exit(EXIT_FAILURE);
}
/* open output file for writing */
out = fopen(argv[2], "w");
if( out==NULL ){
printf("Unable to write to file %sn", argv[2]);
exit(EXIT_FAILURE);
}
/* read words from input file, print on separate lines to output
file*/
while( fscanf(in, " %s", word) != EOF ){
25. fprintf(out, "%sn", word);
}
/* close input and output files */
fclose(in);
fclose(out);
return(EXIT_SUCCESS);
}
Notice that by inserting the leading space in the format string
for fscanf(), we skip all intervening whitespace
characters and thus parse the tokens in the file. (Recall a token
is a substring that (1) contains no white space
characters, and (2) is maximal with respect to (1).)
What to turn in
Write a C program called FileReverse.c that behaves exactly
like the program FileReverse.java that you
wrote in lab 2. Thus FileReverse.c will take two command line
arguments naming the input and output files
respectively (following the FileIO.c example above.) Your
program will read each word in the input file, then
26. print it backwards on a line by itself. For example given a file
called in containing the lines:
abc def ghij
klm nopq rstuv
w xyz
the command % FileReverse in out will create a file called out
containing the lines:
cba
fed
jihg
mlk
qpon
vutsr
w
zyx
Your program will contain a function called stringReverse()
with the heading
27. void stringReverse(char* s)
9
which reverses its string argument. Place the definition of this
function after all preprocessor directives but before
the function main(). Your main function will be almost
identical to FileIO.c above, except that the while
loop will contain a call to stringReverse(word). Although it is
possible to write stringReverse() as a
recursive function, it is not recommended that you do so unless
you are very familiar with C strings and the string
handling functions in the standard library string.h. Instead it is
recommended that you implement a simple
iterative algorithm to reverse the string s (see below).
There is one function from string.h that you will need however,
and that is strlen(), which returns the length
of its string argument. For example the following program
prints out the length of a string entered on the
command line.
/*
28. * charCount.c
* prints the number of characters in a string on the command
line
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(int argc, char* argv[]){
if(argc<2){
printf("Usage: %s some-stringn", argv[0]);
exit(EXIT_FAILURE);
}
printf("%s contains %d charactersn", argv[1], strlen(argv[1])
);
return EXIT_SUCCESS;
}
Remember that a string in C is a char array, not a separate data
type as it is in Java. Recall also that arrays in C
29. cannot be queried as to their length. How then does strlen()
work? Actually a string in C is a little bit more
than a char array. By convention, C strings are terminated by
the null character '0'. This character acts as a
sentinel, telling the functions in string.h where the end of the
string is. Function strlen() counts and returns
the number of characters in its char* (char array) argument up
to (but not including) the null character.
Here is an algorithm in pseudo-code that will perform the string
reversal:
1. Set two variables i and j to i=0 and j=strlen(s)-1
2. Swap the characters s[i] and s[j]
3. Increment i and decrement j
4. Stop if i>=j
5. Otherwise go back to (2)
One more question should be answered here. Are arrays in C
passed by value or by reference? Obviously by
reference, for otherwise functions like stringReverse() would
have no effect on their array arguments outside
the function call. In fact, an array name in C is literally the
address of (i.e. a pointer to) the first element in the
30. array. Arrays, strings, and pointer variables in C will be
discussed in subsequent lab assignments.
Write a Makefile that creates an executable binary file called
FileReverse, and includes a clean utility.
Submit the files: README, Makefile, and FileReverse.c to the
assignment name lab3. Start early and ask
for help if you are at all confused.
FOLLOW THIS INSTRUCTION
Outline, Working Thesis Statement & Starting Draft Due Week
5 and worth 10 0 points
Instructions:
Writing Activity 2 has four sections to complete. You will use
the writing templates in your Webtext to complete them. Once
you complete all templates, you will be able to download your
work and submit it to Blackboard. The Webtext will guide you
through the process.
Section 1
Outline Create an outline. The outline is the plan for what to
include in your essay. See chapter 4 of your Webtext for
information on how to create a great outline.
31. Section 2
Working Thesis Statement This is a one-sentence statement
summarizing the main idea of your essay. It should tell your
reader what your topic is, what your position is on the topic,
and how you will support it. Use the template in chapter 4 of
your Webtext to write your working thesis statement.
Section 3
Starting Draft Use the Webtext writing templates to set up your
essay and write the first two paragraphs. Chapter 5 of the
Webtext will provide you with guidance.
Section 4
Feedback Reflection Use the writing templates in chapter 5 of
the Webtext to: 1. List the feedback you received on writing
activity one 2. Explain how you used feedback from writing
activity one to write activity two 3. Discuss how the feedback
on writing activity one will help you with future writing.
HERE IF FEED BACK FROM INSTRUCTOR
Instructor’s Response: Excellent position. Can you come up
with 3 good reasons to support your position? Your position
plus the 3 reasons will be your thesis.
Instructor’s Response: Excellent points that support your main
idea. One tip: Never end a paragraph with a citation. Always
provide an explanation of the borrowed material.
https://www.youtube.com/watch?v=Y0XYEcaJBM0
1
CMPS 12M
32. Introduction to Data Structures Lab
Lab Assignment 2
The goal of this assignment is to practice using command line
arguments, file input-output, and manipulation
of Strings in java. File input-output and command line
arguments will be necessary to complete some future
assignments.
Command Line Arguments
In a Java program, function main() reads the command line from
which it was called, then stores the tokens
on that line in the args array. Use the following Java program
to create an executable jar file called
CommandLineArguments (see lab1 to learn how to do this)
// CommandLineArguments.java
class CommandLineArguments{
public static void main(String[] args){
int n = args.length;
System.out.println("args.length = " + n);
for(int i=0; i<n; i++) System.out.println(args[i]);
33. }
}
then do %CommandLineArguments zero one two three four and
observe the output. Run it with
several other sets of tokens on the command line. These tokens
are called command line arguments, and can
be used within a program to specify and modify the program's
behavior. Typically command line arguments
will be either strings specifying optional behavior, text to be
processed by the program directly, or names of
files to be processed in some way.
File Input-Output
The java.util package contains the Scanner class, and the java.io
package contains classes PrintWriter and
FileWriter. These classes perform simple input and output
operations on text files. Their usage is illustrated
in the program FileCopy.java below, which merely copies one
file to another, i.e. it provides essentially
the same functionality as the Unix command cp (with respect to
text files only.)
34. // FileCopy.java
// Illustrates file IO
import java.io.*;
import java.util.Scanner;
class FileCopy{
public static void main(String[] args) throws IOException{
Scanner in = null;
PrintWriter out = null;
String line = null;
int n;
// check number of command line arguments is at least 2
if(args.length < 2){
System.out.println("Usage: FileCopy <input file> <output
file>");
2
35. System.exit(1);
}
// open files
in = new Scanner(new File(args[0]));
out = new PrintWriter(new FileWriter(args[1]));
// read lines from in, write lines to out
while( in.hasNextLine() ){
line = in.nextLine();
out.println( line );
}
// close files
in.close();
out.close();
}
}
As you can see, the Scanner constructor takes a File object for
initialization, which is itself initialized by a
36. String giving the name of an input file. The Scanner class
contains (among others) methods called
hasNextLine() and nextLine(). Read the documentation for
Scanner at
http://docs.oracle.com/javase/8/docs/api/
to learn about the proper usage of these methods. The
PrintWriter constructor takes a FileWriter object for
initialization, which is in turn initialized by a String giving the
name of an output file. PrintWriter contains
methods print() and println() that behave identically to the
corresponding methods in System.out,
except that output goes to a file instead of stdout. Note that the
FileWriter initialization can fail if no file
named args[1] exists in the current directory. If it fails, it will
throw an IOException. This is a checked
exception, which cannot be ignored, and therefore function
main() must either catch the exception, or throw
it up the chain of function calls. (In the case of function
main(), the "calling function" is the operating
system). In this example, we deal with this by declaring main()
to throw an IOException, causing the
program to quit if the exception is encountered. Similar
37. comments apply to the initialization of the Scanner
object. See the java documentation for more details.
Compile and run FileCopy.java, and observe that a Usage
statement is printed if the user does not provide
at least two command line arguments. This Usage statement
assumes that the program is being run from an
executable jar file called FileCopy. All of your programs that
take command line arguments should include
such a usage statement.
String Tokenization
A common task in text processing is to parse a string by
deleting the surrounding whitespace characters,
keeping just the discrete words or “tokens” that remain. A
token is a maximal substring containing no
whitespace characters. For instance, consider the preceding
sentence to be a string. The 10 tokens in this
string are: "A", "token", "is", "a", "maximal", "substring",
"containing", "no", "whitespace", "characters.".
Whitespace here is defined to mean spaces, newlines, and tab
characters. This is one of the first tasks that a
compiler for any language such as Java or C must perform. The
source file is broken up into tokens, each of
38. which is then classified as: keyword, identifier, punctuation,
etc. Java's String class contains a method called
split() that decomposes a string into tokens, then returns a
String array containing the tokens as its elements.
Compile and run the following program FileTokens.java
illustrating these operations.
http://docs.oracle.com/javase/8/docs/api/
3
// FileTokens.java
// Illustrates file IO and tokenization of strings.
import java.io.*;
import java.util.Scanner;
class FileTokens{
public static void main(String[] args) throws IOException{
Scanner in = null;
PrintWriter out = null;
String line = null;
39. String[] token = null;
int i, n, lineNumber = 0;
// check number of command line arguments is at least 2
if(args.length < 2){
System.out.println("Usage: FileCopy <input file> <output
file>");
System.exit(1);
}
// open files
in = new Scanner(new File(args[0]));
out = new PrintWriter(new FileWriter(args[1]));
// read lines from in, extract and print tokens from each line
while( in.hasNextLine() ){
lineNumber++;
// trim leading and trailing spaces, then add one trailing
space so
// split works on blank lines
40. line = in.nextLine().trim() + " ";
// split line around white space
token = line.split("s+");
// print out tokens
n = token.length;
out.println("Line " + lineNumber + " contains " + n + "
tokens:");
for(i=0; i<n; i++){
out.println(" "+token[i]);
}
}
// close files
in.close();
out.close();
}
}
What to turn in
41. Write a java program called FileReverse.java that takes two
command line arguments giving the names
of the input and output files respectively (following the
preceding examples). Your program will read each
4
line of input, parse the tokens, then print each token backwards
to the output file on a line by itself. For
example given a file called in containing the lines:
abc defg
hi
jkl mnop q
rstu v
wxyz
the command %FileReverse in out will create a file called out
containing the lines:
cba
gfed
42. ih
lkj
ponm
q
utsr
v
zyxw
Your program will contain a recursive method called
stringReverse() with the following signature:
public static String stringReverse(String s, int n)
This function will return a String that is the reversal of the first
n characters of s. Note that reversing a String
recursively is very similar to reversing an array. Study the
methods in the String class documented at
http://docs.oracle.com/javase/8/docs/api/ to determine how this
might be done. See especially the instance
methods charAt() and substring(), as well as the static method
valueOf(). Base your reversal strategy
on one of the recursive methods reverseArray1() or
43. reverseArray2() discussed in class. Use
stringReverse() to perform the reversal of tokens from the input
file.
Submit the files README, Makefile, and FileReverse.java to
the assignment name lab2. This is not a
difficult assignment, since most of the items mentioned here
should be review, but please don't wait until the
last minute to start.
http://docs.oracle.com/javase/8/docs/api/
1
CMPS 12M
Data Structures Lab
Lab Assignment 1
The purpose of this assignment is threefold: (1) get a basic
introduction to the Andrew File System (AFS)
which is the file system used by the ITS unix timshare, (2) learn
how to create an executable jar file
44. containing a Java program, and (3) learn to automate
compilation and other tasks using Makefiles.
AFS
Logon to your ITS unix timeshare account at unix.ucsc.edu. If
you don't know how to do this, ask for
help at a lab session, or see Lab Assignment 1 from my CMPS
12A/L Spring 2018 webpage:
https://classes.soe.ucsc.edu/cmps012a/Spring18/lab1.pdf
Create a subdirectory within your home directory called cs12b
in which you will keep all your work for
both CMPS 12B and CMPS 12M. Create a subdirectory within
cs12b called lab1. From within lab1
create a subdirectory called private, then set access permissions
on the new directory so that other users
cannot view its contents. Do all this (starting in your home
directory) by typing the lines below. The unix
prompt is depicted here as %, although it may look different in
your login session. The lines without the
unix prompt are the output of your typed commands.
% mkdir cs12b
45. % cd cs12b
% mkdir lab1
% cd lab1
% mkdir private
% fs setacl private system:authuser none
% fs listacl private
Access list for private is
Normal rights:
foobar rlidwka
Here foobar will be replaced by your own cruzid. The last line
of output says that your access rights to
directory private are rlidwka which means: read, list, insert,
delete, write, lock, and administer. In
other words you have all rights in this directory, while other
users have none. If you are unfamiliar with
any unix command, you can view its manual page by typing:
man <command name>. (Do not type the
angle brackets <>.) For instance man mkdir brings up the man
pages for mkdir. Man pages can be very
cryptic, impenetrable even, especially for beginners, but it is
46. best to get used to reading them as soon as
possible. Under AFS, fs denotes a file system command, setacl
sets the access control list (ACL) for a
specific user or group of users, and listacl displays the access
lists for a given directory. The command
% fs setacl <some directory> <some user> <some subset of
rlidwka or all or none>
sets the access rights for a directory and a user. Note that setacl
can be abbreviated as sa and listacl
can be abbreviated as la. For instance do la on your home
directory:
% fs la ~
Access list for /afs/cats.ucsc.edu/users/a/foobar is
Normal rights:
foobar rlidwka
system:authuser l
http://its.ucsc.edu/unix-timeshare/index.html
https://classes.soe.ucsc.edu/cmps012a/Spring18/lab1.pdf
47. 2
The path /afs/cats.ucsc.edu/users/a/foobar will be replaced by
the full path to your home
directory, and your own username in place of foobar. Note that
~ (tilde) always refers to your home
directory, . (dot) always refers to your current working
directory (the directory where you are currently
located) and .. (dot, dot) refers to the parent of your current
working directory. The group
system:authuser refers to anyone with an account on the ITS
unix timeshare. Thus by default, any
user on the system can list the contents of your home directory.
No other permissions are set for
system:authuser however, so again by default, no one else can
read, insert, delete, write, lock, or
administer your files.
Do fs la ~/cs12b and verify that the access rights are the same
for the child directory cs12b as for its
parent, your home directory. Create a subdirectory of private,
call it anything you like, and check its
access rights are the same as for its parent. Thus we see that
child directories inherit their permissions from
the parent directory when they are created. To get a more
48. comprehensive list of AFS commands do fs
help. For instance you will see that fs lq shows your quota
and usage statistics. For more on the
Andrew File System go to
https://en.wikipedia.org/wiki/Andrew_File_System.
Note: Some users may see the group system:anyuser in place of
system:authuser when they do the
above commands. This is because newer accounts were created
with slightly different ACLs than older
accounts. Whatever the ACL is for ~/cs12b/private, set it so
that you have all rights, and other users
have no rights. The distinction between the user groups
system:authuser and system:anyuser is of
no significance to us.
Jar Files
Copy the following file to your lab1 directory. (You can find
this file in the Examples section of the
class webpage.)
// HelloUser.java
49. // Prints greeting to stdout, then prints out some environment
information.
class HelloUser{
public static void main( String[] args ){
String userName = System.getProperty("user.name");
String os = System.getProperty("os.name");
String osVer = System.getProperty("os.version");
String jre = System.getProperty("java.runtime.name");
String jreVer =
System.getProperty("java.runtime.version");
String jvm = System.getProperty("java.vm.name");
String jvmVer = System.getProperty("java.vm.version");
String javaHome = System.getProperty("java.home");
long freemem = Runtime.getRuntime().freeMemory();
long time = System.currentTimeMillis();
System.out.println("Hello "+userName);
System.out.println("Operating system: "+os+" "+osVer);
System.out.println("Runtime environment: "+jre+"
"+jreVer);
50. System.out.println("Virtual machine: "+jvm+" "+jvmVer);
System.out.println("Java home directory: "+javaHome);
System.out.println("Free memory: "+freemem+" bytes");
System.out.printf("Time: %tc.%n", time);
}
}
https://en.wikipedia.org/wiki/Andrew_File_System
3
You can compile this in the normal way by doing javac
HelloUser.java then run it by doing the
command java HelloUser. Java provides a utility called jar for
creating compressed archives of
executable .class files. This utility can also be used to create
an executable jar file that can then be
run by just typing its name at the unix prompt (with no need to
type java first). To do this you must first
create a manifest file that specifies the entry point for program
execution, i.e. which .class file contains
the main() method to be executed. Create a text file called
Manifest containing just one line:
51. Main-class: HelloUser
If you don’t feel like opening up an editor to do this you can
just type
% echo Main-class: HelloUser > Manifest
The unix command echo prints text to stdout, and > redirects
the output to a file. Now do
% jar cvfm HelloUser Manifest HelloUser.class
The first group of characters after jar are options. (c: create a
jar file, v: verbose output, f: second argument
gives the name of the jar file to be created, m: third argument is
a manifest file.) Consult the man pages to
see other options to jar. The second argument HelloUser is the
name of the executable jar file to be
created. The name of this file can be anything you like, i.e. it
does not have to be the same as the name of
the .class file containing function main(). For that matter, the
manifest file need not be called
Manifest, but this is the convention. Following the manifest
file is the list of .class files to be archived.
52. In our example this list consists of just one file:
HelloUser.class. At this point we would like to run
the executable jar file HelloUser by just typing its name, but
there is one problem. This file is not
recognized by Unix as being executable. To remedy this do
%chmod +x HelloUser
As usual, consult the man pages to understand what chmod
does. Now type HelloUser to run the program.
The whole process can be accomplished by typing five lines:
% javac –Xlint HelloUser.java
% echo Main-class: HelloUser > Manifest
% jar cvfm HelloUser Manifest HelloUser.class
% rm Manifest
% chmod +x HelloUser
Notice we have removed the now unneeded manifest file. Note
also that the –Xlint option to javac enables
recommended warnings. The only problem with the above
procedure is that it’s a big hassle to type all
those lines. Fortunately there is a unix utility that can automate
53. this and other processes.
Makefiles
Large programs are often distributed throughout many files that
depend on each other in complex ways.
Whenever one file changes all the files depending on it must be
recompiled. When working on such a
program it can be difficult and tedious to keep track of all the
dependencies. The Unix make utility
automates this process. The command make looks at
dependency lines in a file named Makefile. The
dependency lines specify relationships between source files by
indicating a target file that depends on one
or more prerequisite files. If a prerequisite has been modified
more recently than its target, make updates
4
the target file based on construction commands that follow the
dependency line. make will normally stop
if it encounters an error during the construction process. Each
dependency line has the following format.
target: prerequisite-list
54. construction-commands
The dependency line is composed of the target and the
prerequisite-list separated by a colon. The
construction-commands may consist of more than one line, but
each line must start with a tab
character.
Start an editor and copy the following lines into a file called
Makefile.
#-------------------------------------------------------------------------
-----
# A simple Makefile
#-------------------------------------------------------------------------
-----
HelloUser: HelloUser.class
echo Main-class: HelloUser > Manifest
jar cvfm HelloUser Manifest HelloUser.class
rm Manifest
chmod +x HelloUser
HelloUser.class: HelloUser.java
55. javac -Xlint HelloUser.java
clean:
rm -f HelloUser HelloUser.class
submit: README Makefile HelloUser.java
submit cmps012b-pt.w19 lab1 README Makefile
HelloUser.java
Anything following # on a line is a comment and is ignored by
make. The second line says that the target
HelloUser depends on HelloUser.class. If HelloUser.class
exists and is up to date, then
HelloUser can be created by doing the construction commands
that follow. Remember that all indentation
is accomplished via the tab character. The next target is
HelloUser.class which depends on
HelloUser.java. The next target clean is what is sometimes
called a phony target since it doesn’t
depend on anything and just runs a command. Likewise the
target submit does not compile anything, but
does have some dependencies. Any target can be built (or
executed if it is a phony target) by doing make
56. <target name>. Just typing make creates the first target listed
in the Makefile. Try this by doing make
clean to get rid of all your previously compiled stuff, then do
make again to see it all created again.
Your output from make should look something like:
% make
javac -Xlint HelloUser.java
echo Main-class: HelloUser > Manifest
jar cvfm HelloUser Manifest HelloUser.class
added manifest
adding: HelloUser.class(in = 1577) (out= 843)(deflated 46%)
rm Manifest
chmod +x HelloUser
The make utility allows you to create and use macros within a
Makefile. The format of a macro definition
is ID = list where ID is the name of the macro (by convention
all caps) and list is a space separated
list of filenames. Then the expression $(list) below the
definition in the Makefile, refers to this list of
57. 5
files. Move your existing Makefile to a temporary file, then
start your editor and copy the following lines
to a new file, again called Makefile.
#-------------------------------------------------------------------------
-----
# Another Makefile with macros
#-------------------------------------------------------------------------
-----
JAVASRC = HelloUser.java
SOURCES = README Makefile $(JAVASRC)
MAINCLASS = HelloUser
CLASSES = HelloUser.class
JARFILE = HelloUser
SUBMIT = submit cmps012b-pt.w19 lab1
all: $(JARFILE)
$(JARFILE): $(CLASSES)
58. echo Main-class: $(MAINCLASS) > Manifest
jar cvfm $(JARFILE) Manifest $(CLASSES)
rm Manifest
chmod +x $(JARFILE)
$(CLASSES): $(JAVASRC)
javac -Xlint $(JAVASRC)
clean:
rm $(CLASSES) $(JARFILE)
submit: $(SOURCES)
$(SUBMIT) $(SOURCES)
Notice that the definition of a macro can contain other macros
in its right hand side, as in the list SOURCES
above. Run this new Makefile and observe that it is equivalent
to the previous one. The macros define text
substitutions that happen before make interprets the file. Study
this new Makefile until you understand
exactly what substitutions are taking place. Now create your
own Hello program and call it
59. HelloUser2.java. It can say anything you like, but just have it
say something different from the original.
Add HelloUser2.java to the JAVASRC list, add
HelloUser2.class to the CLASSES list and change
MAINCLASS to HelloUser2. Also change the macro JARFILE
to just Hello (emphasizing that the jar
file can have any name.) Remember that all indentation in a
Makefile must be accomplished with the tab
character, not spaces, or the make utility will not work.
#-------------------------------------------------------------------------
-----
# Yet another Makefile with macros
#-------------------------------------------------------------------------
-----
JAVASRC = HelloUser.java HelloUser2.java
SOURCES = README Makefile $(JAVASRC)
MAINCLASS = HelloUser2
CLASSES = HelloUser.class HelloUser2.class
JARFILE = Hello
SUBMIT = submit cmps012b-pt.w19 lab1
60. all: $(JARFILE)
$(JARFILE): $(CLASSES)
echo Main-class: $(MAINCLASS) > Manifest
jar cvfm $(JARFILE) Manifest $(CLASSES)
rm Manifest
chmod +x $(JARFILE)
6
$(CLASSES): $(JAVASRC)
javac -Xlint $(JAVASRC)
clean:
rm $(CLASSES) $(JARFILE)
submit: $(SOURCES)
$(SUBMIT) $(SOURCES)
This new Makefile compiles both HelloUser classes (even
though neither one depends on the other.) Notice
61. however the entry point for program execution has been
changed to function main() in your program
HelloUser2.java. Macros make it easy to make changes like
this, so learn to use them. To learn more
about Makefiles follow the links posted on the class webpage.
We've discussed three Makefiles in this project. If you rename
them Makefile1, Makefile2 and
Makefile3 respectively (since you can't have three files with the
same name), you'll find that the make
command does not work since a file called Makefile no longer
exists. Instead of renaming files to run
the Makefile you want, you can use the -f option to the make
command, and specify the name of your
Makefile. For instance
% make -f Makefile2
runs Makefile2. If you want to specify something other than the
first target, place it after the file name
on the command line. For instance
% make –f Makefile3 clean
62. runs the clean target in Makefile3.
What to turn in
All files you turn in for this and other assignments (in both 12B
and 12M) should begin with a comment
block giving your name, cruzid, class (12B or 12M), date, a
short description of its role in the project, the
file name and any special instructions for compiling and/or
running it. Create one more file called
README which is just a table of contents for the assignment.
In general README lists all the files being
submitted (including README) along with any special notes to
the grader. Use what you've learned in
this project to write a Makefile that creates and archives both
.class files (HelloUser.class and
HelloUser2.class) in an executable jar file called Hello, and that
runs function main() from
HelloUser2.class. It should also include clean and submit
utilities, as in the above examples. Also
add a check utility that checks that the project was properly
submitted.
See the webpage for instructions on using the submit command
and for checking that a project was
63. properly submitted. Submit the following files to the
assignment name lab1:
README described above
Makefile described above
HelloUser.java unchanged from above
HelloUser2.java described above
You can either type the submit command directly:
7
% submit cmps012b-pt.w19 lab1 README Makefile
HelloUser.java HelloUser2.java
or use the Makefile itself:
% make submit
This is not a difficult assignment, especially if you took CMPS
12A from me (see lab4 from the Spring
2018 offering of that class), but start early and ask questions if
anything is unclear.