COMP1911 23T2 Introduction to Programming

Objectives

In this Lab, you will practise:

Preparation

Before the lab you should re-read the relevant lecture slides and their accompanying examples

Getting Started

Login and run following commands inside a Unix terminal

Create a new directory for this lab called lab07 by typing:

mkdir lab07
Change to this directory by typing:
cd lab07

Introduction

Some of the exercises in the labs require you to read characters using the getchar function.

getchar reads the next character from standard input and returns it. If getchar is unable to read a character it return the special value EOF.

When input is coming from a file, getchar will return EOF after the last character in the file is read.

When input is coming from a (Linux/OSX) terminal, you can indicate no more characters can be read by typing Ctrl+D. This will cause getchar to return EOF

In some of this week's lab exercises you will find it convenient to put the test input in a file, rather than type it every time you want to test the program.

You can use a < character to indicate to the shell that you want to run a program taking its input from a file.

So for example you might create the file input.txt with gedit and then run a.out takes its input from the file rather the terminal:

gedit input.txt &
$ ./a.out <input.txt

Exercise 1: String Length

Write a C program strlen.c which reads characters from stdin and prints the number of characters in that string. Most of the program is complete for you, you simply need to complete the strlength function.

Template file:
// Author: Hayden Smith 2019

#include 

#define MAXLINE 1024

int strlength(char *);

int main(void) {
    char line[MAXLINE];
    fgets(line, MAXLINE, stdin);
    printf("Strlen is %d\n", strlength(line));
}

int strlength(char *s) {
    // TODO
    return 0;
}

$ ./strlen
cs1911
6
 $ 1911 autotest lab07 strlen.c
Sample solution for strlen.c
// Author: Hayden Smith 2019

#include <stdio.h>

#define MAXLINE 1024

int strlength(char *);

int main(void) {
    char line[MAXLINE];
    fgets(line, MAXLINE, stdin);
    printf("Strlen is %d\n", strlength(line));
}

int strlength(char *s) {
    int i = 0;
    while (s[i] != '\0') {
        i++;
    }
    return i - 1;
}

Exercise 2: Devowelling

Write a C program devowel.c which reads characters from its input and writes the same charcters to its output, except it does not write lower case vowels ('a', 'e','i', 'o', 'u').

Your program should stop only at the end of input.

For example:

$ ./devowel
The quick brown fox jumped over the lazy dog.
Th qck brwn fx jmpd vr th lzy dg.
It was the best of times. It was the worst of times.
It ws th bst f tms. It ws th wrst f tms.

The only functions you are allowed to use are getchar and putchar.

Don't use printf, scanf or fgets.

As usual autotest is available to test your program.

 $ 1911 autotest lab07 devowel.c
Sample solution for devowel.c
#include <stdio.h>

int main(void) {
    int c;

    // getchar returns an int which will contain either
    // the ASCII code of the character read or EOF

    c = getchar();
    while (c != EOF) {

        // test if ch is not a  vowel
        if (c != 'a' && c != 'e' && c != 'i' && c != 'o' && c != 'u') {
            putchar(c);
        }

        c = getchar();
    }

    return 0;
}

Exercise 3: Palindrome

A palindrome is a sequence which is the same forwards as backwards.

Write a program, palindrome.c, which reads a string and tests if it is a palindrome.

For example:

$ ./palindrome
Enter a string: kayak
String is a palindrome
$ ./palindrome
Enter a string: canoe
String is not a palindrome

Don't use scanf - use fgets to read the string.

Note, your program needs to read only one string - it doesn't have to read until the end of input.

You can assume lines contain at most 4096 characters.

As usual autotest is available to test your program.

 $ 1911 autotest lab07 palindrome.c
Sample solution for palindrome.c
//
// Read a string and then indicate if it is a palindrome
// written by Andrew Taylor andrewt@cse.unsw.edu.au
// April 2014 as a COMP1911 lab exercise
//

#include <stdio.h>

#define MAXLINE 4094

int isPalindrome(char line[]);
int findRightMostIndex(char line[]);

int main(int argc, char *args[]) {
    char line[MAXLINE];

    printf("Enter a string: ");
    fgets(line, MAXLINE, stdin);
   
    if(isPalindrome(line)){
        printf("String is a palindrome\n");
    } else {
        printf("String is not a palindrome\n");
    }
     
    return 0;
}

//Returns 1 if the line is a palindrome and 0 otherwise
int isPalindrome(char line[]){
    int left = 0;

    int right = findRightMostIndex(line);
    //The empty string is a palindrome
    if(right == -1) {
        return 1;
    }
    while (left < right) {
        if (line[left] != line[right]) {           
            return 0;
        }
        left = left + 1;
        right = right - 1;
    }
    return 1;
}

//Finds right most index of string or that last character 
// up to the first newline
//returns -1 if there are no characters or if it starts with a newline
int findRightMostIndex(char line[]){
    int right = 0;
    while (line[right] != '\0' && line[right] != '\n') {
        right =  right + 1;
    }
    
    right = right - 1;
    return right;
}

Exercise 4: Printing Characters One Per Line - stdin

Write a C program, one.c, which reads in one string from the user and writes out the characters one per line to a new file in the same directory called data.txt. The output from your program should look like this:
$ ./one
Enter a string: Hello
Then when you try and print the file out you expect this:
H
e
l
l
o

Don't use scanf
use fgets to read the string.

Note, your program needs to read only one string - it doesn't have to read until the end of input.

You can assume lines contain at most 4096 characters.

Sample solution for one.c
#include <stdio.h>

#define MAXLINE 4094

int main(void) {
    char line[MAXLINE];
    int  i;
    FILE *stream;

    printf("Enter a string: ");
    fgets(line, MAXLINE, stdin);

    i = 0;
    stream = fopen("data.txt", "w");
    while (line[i] != '\n' && line[i] != '\0') {
        fprintf(stream, "%c\n", line[i]);
        i = i + 1;
    }

    return 0;
}


(Optional) Challenge Exercise 1: Modifying Palindrome

Modify your Palindrome program (call it palindrome1.c) so characters which are not letters are ignored and difference between upper case and lower case are ignored. For example:

$ ./palindrome1
Enter a string: Do geese see God?
String is a palindrome
$ ./palindrome1
Enter a string: Do ducks see God?
String is not a palindrome
$ ./palindrome1
Enter a string: Madam, I'm Adam
String is a palindrome
$ ./palindrome1
Enter a string: Madam, I'm Andrew
String is not a palindrome

Hint: if you #include <ctype.h> you can use a C library function named tolower to convert a character to lower case.

 $ 1911 autotest lab07 palindrome1.c
Sample solution for palindrome1.c
//
// Read a string and then indicate if it is a palindrome
// only lower case alphabetic letters are considered
// written by Andrew Taylor andrewt@cse.unsw.edu.au
// April 2014 as a COMP1911 lab exercise
//

#include <stdio.h>
#include <ctype.h>

#define MAXLINE 4094

int isPalindrome(char line[]);
int findRightMostIndex(char line[]);

int main(int argc, char *args[]) {
    char line[MAXLINE];
    int  left, right ;

    printf("Enter a string: ");
    fgets(line, MAXLINE, stdin);

    if(isPalindrome(line)){
        printf("String is a palindrome\n");
    } else {
        printf("String is not a palindrome\n");
    }
 
    return 0;
}

//Finds right most index of string or that last character 
// up to the first newline
//returns -1 if there are no characters or if it starts with a newline
int findRightMostIndex(char line[]){
    int right = 0;
    while (line[right] != '\0' && line[right] != '\n') {
        right =  right + 1;
    }
    
    right = right - 1;
    return right;
}

//Returns 1 if the line is a palindrome and 0 otherwise
int isPalindrome(char line[]){
    int left = 0;

    int right = findRightMostIndex(line);
    //The empty string is a palindrome
    if(right == -1) {
        return 1;
    }
    
    while (left < right) {
        line[left] = tolower(line[left]);
        line[right] = tolower(line[right]);
        if (line[left] < 'a' || line[left] > 'z') {
            left = left + 1;
        } else if (line[right] < 'a' || line[right] > 'z') {
            right = right - 1;
        } else if (line[left] != line[right]) {          
            return 0;
        } else {
           left = left + 1;
           right = right - 1;
        }
    }
    return 1;
}

(Optional) Challenge Exercise 2: Chessboard

Copy the program black.c from the course account to your directory by typing:

cp ~cs1911/public_html/23T2/code/black.c chessboard.c
Compile it, run it and then type
display black.bmp 

At the moment all you should see is a 512x512 completely black bitmap image.

Modify the file so it generates a chessboard image consisting of 16x16 black and white squares. The name of the bmp file it generates should be chessboard.bmp

Your image must look exactly like this

Hint: You should only need to modify the generateData function and change the name of the BMP_FILE. Hint: Look at the example code (Wee7 lecture code) to create different colours and a digaonal bmpDemo.c.

Sample solution for chessboard.c
// An advanced example
// To compile, run and view the finished product type 
 
// dcc -o chessboard chessboard.c
// ./black

// display chessboard.bmp


// An example of creating a bitmap file with code. 
// We generate the bytes using a while loop. 
// The bitmap is 512 x 512.
// The bitmap file has a header, with data that includes 
// the size of the file and other things we do not need to worry about. 
// After the header, there are data bytes that represent the pixels.
// Each pixel is represented by 3 bytes. 
// One for blue, one for green and one for red. 
// The first 3 bytes in the pixel data represent
// the bottom left corner of the file.

//  Created by Richard Buckland on 14/04/11, edited 5/4/14
//   Edited by Angela Finlayson on 30/04/2017
  
 

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
 
#define BYTES_PER_PIXEL 3
#define BITS_PER_PIXEL (BYTES_PER_PIXEL*8)
#define NUMBER_PLANES 1
#define PIX_PER_METRE 2835
#define MAGIC_NUMBER 0x4d42
#define NO_COMPRESSION 0
#define OFFSET 54
#define DIB_HEADER_SIZE 40
#define NUM_COLORS 0
 
#define SIZE 512
#define BMP_FILE "chessboard.bmp"

#define SQUARE_SIZE 16
 
void writeHeader (FILE *file);
void generateData(FILE *file);
void writePixel(unsigned char b, unsigned char r, unsigned char g, FILE * f); 

int main (void) {
 
   FILE *outputFile;
   
 
   outputFile = fopen(BMP_FILE, "wb");

   if ( outputFile == NULL ){
       fprintf(stderr,"Could not open file\n");
   }


   writeHeader(outputFile);  
   
   generateData(outputFile);
  
   
   fclose(outputFile);
 
   return EXIT_SUCCESS;
}


// Change this function to create different images!
// Alternating between black and white in a chessboard pattern, every SQUARE_SIZE pixels
void generateData(FILE * f){
   unsigned char blue = 0;
   unsigned char green = 0;
   unsigned char red = 0;
  
   int row = 0;
   int col = 0;
   while (row < SIZE) {
      col = 0;
      while(col < SIZE){          
         
          writePixel(blue,green,red,f);
          
          col++;
           if( col % SQUARE_SIZE == 0){ 
              if(blue == 255 ){
                  blue = 0;
                  green = 0;
                  red = 0;
              } else {
                  blue = 255;
                  green = 255;
                  red = 255;
              }
          }
      }
      
      row++;
      if( row % SQUARE_SIZE == 0){ 
              if(blue == 255 ){
                  blue = 000;
                  green = 0;
                  red = 0;
              } else {
                  blue = 255;
                  green = 255;
                  red = 255;
              }
      }
   }
}

// The colour of each pixel is determined by its 
// blue, green and red values. 
// So each pixel needs 3 bytes of data written to the file
void writePixel(unsigned char b, unsigned char g, unsigned char r, FILE * f){
            fwrite (&b, sizeof (unsigned char), 1, f);
            fwrite (&g, sizeof (unsigned char), 1, f);
            fwrite (&r, sizeof (unsigned char), 1, f);
}
 
// Do not worry if you do not understand this code and what all the 
// values are for. It is just writing a header in the bmp and follows 
// the standard format For more info if you are interested see 
// https://en.wikipedia.org/wiki/BMP_file_format

void writeHeader (FILE *file) {
 
 
   unsigned short magicNumber = MAGIC_NUMBER;
   fwrite (&magicNumber, sizeof magicNumber, 1, file);
 
   unsigned int fileSize = OFFSET + (SIZE * SIZE * BYTES_PER_PIXEL);
   fwrite (&fileSize, sizeof fileSize, 1, file);
 
   unsigned int reserved = 0;
   fwrite (&reserved, sizeof reserved, 1, file);
 
   unsigned int offset = OFFSET;
   fwrite (&offset, sizeof offset, 1, file);
 
   unsigned int dibHeaderSize = DIB_HEADER_SIZE;
   fwrite (&dibHeaderSize, sizeof dibHeaderSize, 1, file);
 
   unsigned int width = SIZE;
   fwrite (&width, sizeof width, 1, file);
 
   unsigned int height = SIZE;
   fwrite (&height, sizeof height, 1, file);
 
   unsigned short planes = NUMBER_PLANES;
   fwrite (&planes, sizeof planes, 1, file);
 
   unsigned short bitsPerPixel = BITS_PER_PIXEL;
   fwrite (&bitsPerPixel, sizeof bitsPerPixel, 1, file);
 
   unsigned int compression = NO_COMPRESSION;
   fwrite (&compression, sizeof compression, 1, file);
 
   unsigned int imageSize = (SIZE * SIZE * BYTES_PER_PIXEL);
   fwrite (&imageSize, sizeof imageSize, 1, file);
 
   unsigned int hResolution = PIX_PER_METRE;
   fwrite (&hResolution, sizeof hResolution, 1, file);
 
   unsigned int vResolution = PIX_PER_METRE;
   fwrite (&vResolution, sizeof vResolution, 1, file);
 
   unsigned int numColors = NUM_COLORS;
   fwrite (&numColors, sizeof numColors, 1, file);
 
   unsigned int importantColors = NUM_COLORS;
   fwrite (&importantColors, sizeof importantColors, 1, file);
 
}

Note: There is no autotest for this exercise. You must demonstrate your program by displaying the image it produces.

Submission/Assessment

When you are satisfied with your work, ask your tutor to assess it. You are to submit it electronically by typing (run this command in your lab07 directory):
give cs1911 lab07 strlen.c devowel.c one.c palindrome.c palindrome1.c chessboard.c
Submit advanced exercises only if you attempt the advanced exercise.

Remember the lab assessment guidelines - if you don't finish the exercises you can finish them in your own time, submit them by 19:59:59 Sunday using give and ask ask tutor to assess them at the start of the following lab.

You can also just run the autotests without submitting by typing

1911 autotest lab07