logo

UTK Notes


Lab 5 - Speeding Ticket Calculator

Pre-lab

Just to make your life a little easier, you can run the following command (on hydra/tesla) in the same directory as where your program is located to download the input file used in this lab, to save you the trouble of having to copy it in yourself.

1
curl https://pastebin.com/raw/whKWBRR6 --output ticket

or

1
curl https://utknotes.pisaucer.com/CS102/EthanNotes/Lab05/ticket.txt --output ticket

This will save the file as ticket in your current directory. Then when you run your program, you can just type ticket as the input file name when prompted. (There’s no need for .txt at the end of the file name. Linux doesn’t care about extensions other than for transparency – but you can add .txt if you want. (I think you actually have to for Windows computers) You can also download from the link directly if you want the input file on your local machine and don’t have access to the curl command. Just rename the file to ticket and move it to the same directory as your program.

Similarly, you can automate the testing of your programming by

1. Downloading the following file (either via curl or the link I’ll provide). Make sure you’ve already downloaded the ticket file and moved it into whatever directory your program is in (if it didn’t download to there already)

1
curl https://pastebin.com/raw/QBQAQr0i --output test
1
curl https://utknotes.pisaucer.com/CS102/EthanNotes/Lab05/test.txt --output test

(or from the link here, rename it to test and move it into the same directory as your program)

2. Subsequently running the following command to test your program

1
./lab5 < test

Running ./lab5 < test will run your program and automatically feed it the input from the test file. This will save you the trouble of having to manually type in the input every time you want to test your program. The input in the test file is the sample input used on the Canvas assignment page.


Introduction

In this lab, we’ll be calculating a fine for a number of speeding tickets, and then outputting that fine as a report. The caveat to this lab is that the ticket we read is an actual file on the computer. So we’ll be familiarizing ourselves with a few new concepts, such as reading from a file, and using the ifstream class.

Step 1: Reading input

The first part of this program is prompting the user for the information needed to calculate the fine. The program should prompt the user for the following information, and store them in the appropriate variables:

  • Ticket File: The name of the file containing the ticket information. This will be the name of an actual file on the computer. More on that later. Its type is string.

PLEASE READ THIS

I cannot stress enough what I’m trying to say here. So many students get tripped up when it comes to reading files. We are prompting the user for a ticket file. More specifically, we are prompting for the user to enter the name of the ticket file. If you had a file on your computer called ticket.txt for example, and you wanted to run your program for that file, then you would input ticket.txt when prompted for the ticket file as shown below. That’s all it is. We get the file NAME, and then we will refer to that file later on using methods built in to C++, which just so happens to need the file name, so we store it in a string. You won’t modify it at all, we are simply trusting the user to type in the name of a file on the computer. Note that for the lab machines (linux machines), an extension like .txt is not needed when you’re creating files. The file could exist as ticket as opposed to ticket.txt, it’s up to whatever you name the file. Just know that .txt is not necessary. On Windows however, the file will need to have a .txt extension when created. This is just a difference in operating systems.

  • Report Start Date: Name is self explanatory. Just know that the format is mm dd yyyy, and it should actually consist of 3 separate values, each of which is an int.

  • Report End Date: Same as above, but for the end date.

Here’s an example of the prompt output

1
2
3
Enter a ticket file: ticket // ticket file is "ticket"
Enter report start date (mm dd yyyy): 7 1 2017 // three int's separated by spaces
Enter report end date   (mm dd yyyy): 8 11 2018

You should already know how to read info from stdin (the name we use for the keyboard basically). We can just use cin. A little hint though to make your life easier is that you can read multiple values into multiple variables with just one cin. This is because cin by default recognizes spaces as “delimiters”, which is just a fancy word for things that separate other things.

The example below shows a use case for this:

Example

1
2
3
int v1, v2, v3;

cin >> v1 >> v2 >> v3;

This will read 3 values from stdin and store them in v1, v2, and v3 respectively. You can use this to read the 3 values for the report start date in one line.

Step 2: File Input

As previously mentioned, the ticket file is an actual file on the computer. This means that we’ll need to use a new class called ifstream to read from the file. We’ll do that using the fstream library.

PLEASE READ THIS

I’m going to reiterate this again: The file we’re reading will be an actual file on the computer. The way we refer to our file in our program is through its name, just like you would refer to any file on your computer yourself. We’ve stored the name of the file in a string variable from the previous step, and now we can use the ifstream class to open the file and read from it. You don’t have to worry about the name of the file at all – the user will provide it for you. Your job is JUST to do as I describe below.

And I’ll just mention now, during the parsing of the file, once you’ve obtained the date, you should skip any operation pertaining to that ticket if it doesn’t fall within the date range specified in the initial prompt. More on that later.

Input File Format

Luckily the input file will adhere to a very specific format, so we can consistently read each a file the same way every time and parse it accordingly. The format is as follows (excluding the brackets):

1
<citation number> <month> <day> <year> <clocked speed> <speed limit> <type of road>
  • Citation Number: a string that represents the citation number (yes, it’s a string). This is the first value in the file.
  • Month: An int that represents the month the ticket was issued. This is the second value in the file.
  • Day: An int that represents the day the ticket was issued. This is the third value in the file.
  • Year: An int that represents the year the ticket was issued. This is the fourth value in the file.
  • Clocked Speed: An int that represents the speed the driver was clocked at. This is the fifth value in the file.
  • Speed Limit: An int that represents the speed limit of the road. This is the sixth value in the file.
  • Type of Road: A char that represents the type of road the ticket was issued on. This is the seventh value in the file.

One caveat here is that the year can be in the format of yyyy or yy. If it’s in the format of yy, then it should be interpreted as 20yy. For example, if the year is 17, then it should be interpreted as 2017, since in the final output we print the full year, and for the purposes of this lab ever year is assumed to be within the 21st century..

Here’s an example of an input file:

Example

1
2
3
4
E059564 8 12 2018 89 55 i
E515522 7 3 2017 105 50 r
E712221 6 4 2015 200 25 h
E219221 12 25 17 2000 10 p

Our program will go through each line of the ticket file and parse it accordingly. We do this using the fstream library, and more specifically the ifstream class.

Reading The File & The fstream Library

Similar to <iostream>, <fstream> is basically a subset of the former. It provides very similar methods for you to interact with files the same way you would with cin or cout. For more information about the library, you can check out the documentation here. But I’ll give a brief description of the bulk of it below.

cin is to iostream as ifstream is to fstream. You can think of cin as just a pre-defined keyword that we can use to easily read from stdin (the technical term for the keyboard, basically). ifstream is almost the exact same as cin, but there’s a little setup involved because it’s not provided for us by default like cin is.

In general, these are the steps to reading a file:

  1. Create an ifstream object. This is the object that will be used to read from the file. You can think of it as a file handle.

  2. Open the file using the ifstream object. This is done using the open() method. This method takes in a string that represents the name of the file. This is the same name that the user entered in the prompt.

  3. Read from the file using the ifstream object. This is done using the >> operator. This operator is overloaded to read from a file, and it works the same way as cin.

  4. Close the file once we are done reading using the ifstream object. This is done using the close() method. This method takes no arguments.

First though, we’ll need to include the library in our program

1
#include<fstream>

Although you should already know how to do these things, here are the steps broken down a little more

1. creating the ifstream object

1
2
3
4
5
6
7
// can be invoked in a number of ways

ifstream if; // creates a blank ifstream handle

// or 

ifstream if("file_to_open"); /* creates an ifstream handle that automatically opens the file */

Note

In the above example, "file_to_open" is the name of the file we want to open. In your code, you’ll replace that string literal (i.e. an “in-place” string) with the string variable that holds the name of the file you prompted the user for in step 1.

2. Opening the File

1
2
3
4
5
6
7
8
9
10
/ opens the file.
// can be skipped if the ifstream object was created with a file name
if.open("file_to_open");

// check to make sure the file is open by our program.
// if it isn't, then we can't read from it

if (!if.is_open()) {
    return 1; // exit the program
}

I’ll not here that if.is_open() returns true if the file opened successfully, and false otherwise. This is why we check for the opposite of that in the if statement. If the file is not open, then you need to print this exactly

1
Unable to open <file>.

where file is the name of the file that the user entered in the prompt. Then you need to exit the program.

3. reading from the file

After the file is opened, reading from the file is the exact same as reading with cin. Again, the only difference is that we use the ifstream object instead of cin, because the contents of a file are being read instead of the keyboard.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// read from the file.
// can be done in a number of ways

int v1, v2, v3;

// reads 3 values from the file and stores them in v1, v2, and v3
if >> v1 >> v2 >> v3; 

// or

int v1, v2, v3;

if >> v1; // reads 1 value from the file and stores it in v1

if >> v2; // reads 1 value from the file and stores it in v2

if >> v3; // reads 1 value from the file and stores it in v3

4. Closing the File

1
2
3
// closes the file

if.close();

As we read each ticket entry in the file, we’ll need to store them in their respective variables, and calculate/print the fee for each ticket. We’ll do this in the next step.

Step 3: Calculating The Ticket Fee

Now that we have the ticket information, we can calculate the fee for each ticket. The fee is calculated using two variables

  1. The initial fee of the ticket, which is calculated by subtracting the speed limit from the clocked speed.

  2. A multiplier that is determined based on the type of road the ticket was issued on, and then multiplied by the initial fee.

The multipliers are pre-defined and supplied by the Canvas assignment page. We’ll store the multipliers as const double variables, since they will never change. This is a good practice to follow, since it makes it easier to change the values later on if needed.

Multipliers

1
2
3
4
Interstate multiplier:  5.2341
Highway multiplier:     9.4826
Residential multiplier: 17.1544
None of the above:      12.8334

When reading the file, we’ll need to store the type of road the ticket was issued on. We’ll do this by storing it in a char variable. We’ll then use a switch statement to determine the multiplier based on the value of the char variable.

This part is pretty straightforward, so I won’t go into too much detail.

Step 4: Printing The Ticket Information

Now that we have the ticket information and the fee, we can print the ticket information (to the console). The output should be in the following format:

1
<citation> <2-digit day>-<3-character Month>-<4-digit Year> $<fine>

Here’s an example of the output for the example file used earlier:

Input File (from earlier)

1
2
3
4
E059564 8 12 2018 89 55 i
E515522 7 3 2017 105 50 r
E712221 6 4 2015 200 25 h
E219221 12 25 17 2000 10 p

Output Corresponding to the Input File

1
2
E515522    03-Jul-2017 $   943.49 
E219221    25-Dec-2017 $ 25538.47

Notice that the output contains some of the same variables from the input file, however there are a few caveats to the format, which will be accomplished using some logic and string manipulation.

  • The citation number is 10-wide, left justified.

  • The date is formatted as dd-MMM-yyyy, where MMM is the 3 letter abbreviation for the month. You’ll write the month from a const array of strings that contains the 3-letter abbreviation for each month. (Indexing this should be self explanatory)

  • The fine is formatted to 2 decimal places, filled 9-wide, and right justified.

  • Extremely Important - The resulting report only contains tickets that were issued between the start and end date specified in the initial prompt.

Read the format specifications carefully, because the output needs to adhere to it exactly. If the ticket file contains a date that is 8 5 13, then it should be printed as 05-Aug-2013. i.e. you’ll need to modify the numbers given accordingly. The input could also be 8 12 2013, in which case you won’t have to modify the numbers at all since 12 is already 2 digits wide, and 2013 is already 4 digits wide. There’s a number of ways you can accomplish this. As I’ve already briefly mentioned, your final output will only include the tickets that fall between the start and end date specified in the initial prompt. This means that you’ll need to compare the date of each ticket to the start and end date. If the ticket date is between the start and end date (inclusive), then it should be printed. If it isn’t, then it should be skipped. This is probably the most difficult part of the assignment in terms of the math required, but it’s still pretty straightforward.

Comparing Dates

There’s a bunch of ways to compare dates. Some of them are simpler than others but maybe not as intuitive, whereas others are more verbose but easier to understand. I’m not going to describe the methods here outside of a couple of hints to lead you in the right direction (there’s always office hours/Discord/Google if you get stuck)

hints

  1. More verbose but easier to understand: You can compare the date you’ve read to the date range by checking each individual component of the date, starting at the widest scope (year) and working your way down to the smallest scope (day).

  2. Less verbose but more difficult to understand: You can convert the date to a single number, and compare the number to the date range. This might not be as intuitive, but it’s less typing than the former.

  3. It’s possible you could use code from the STL (like std::chrono), but you’ll have to discuss that with the course instructor as to whether or not it’s allowed. If it’s not and you end up using it, you’ll be penalized for it.

Just keep in mind that the date range given is inclusive. So if a ticket date is exactly equal to the start/end date, then it still falls within the range.

Requirements

As usual your program needs to adhere to the general requirements like proper indentation, variable naming, header, etc. But other than that, there are a few specific requirements that you need to follow.

  1. You must use iomanip to restrict the day output to 2 digits, and the year output to 4 digits.

  2. You must use iomanip to restrict the fine output to 2 decimal places.

  3. You must use a const string array (NOT a vector) to store the 3-letter month abbreviations.

  4. You must use const double’s for each fine multiplier and a switch statement to determine the multiplier used in the calculation based on the road type.

  5. You must use iomanip for formatting the output (e.g. justification)

  6. All fines must be ≥ $0.00. If the fine is less than $0.00, then round to $0.

  7. If you attempt to open a file that does not exist, then you should print an error message and exit the program.

MAKE SURE IT COMPILES & RUNS

The program you turn in must compile and run on the lab machines using this exact command:

1
g++ -Wall -O0 -g -std=c++11 -o lab5 lab5.cpp

If it doesn’t compile with that exactly, you will almost certainly get a 0. If you’re having trouble getting it to compile, then you should ask for help on Discord or in office hours.


Extra stuff if you’re interested

This is just some extra stuff for those of you interested in learning about the why’s and how’s of the assignment. If you’re not interested, then that’s it! You’re done! You can stop reading now.

Const

You’ll notice that we’ve used const a lot in this assignment. This is a good practice to follow, since it makes it easier to change the values later on if needed. For example, if you wanted to change the fine multiplier for i (interstate) from 5.2341 to 5.2342, you’d only have to change it in one place. If you didn’t use const, you’d have to change it in 3 places (the switch statement, the array, and the cout statement). Of course, you could just keep the variables as double’s and not use const, but then you’d have to be careful not to accidentally change the value of the variable. Keeping it const guarantees that the only way it changes is if you physically type in a different number for that variable. Honestly, you should strive to use const as much as possible in your code, as it will reduce the headaches you have down the line. If you’re interested in learning more about const, you can read about it here.


References

I’ve compiled a list of references if you get stuck on something and want easy access to information that might help you.