Hello, dear friend, you can consult us at any time if you have any questions, add WeChat: daixieit

CSE 30 Spring 2024 PA 4 - (Version 1.10)

Due Date: Wednesday May 1, 2024 @ 11:59PM

Final Submission Date: Saturday May 4, 2024 @ 11:59PM

Difficulty Gauge    1 = Easy    2    3    4    5 = Hard

Grading (30 points total) What You Need To Do

This assignment must be completed individually.

3 points off for each late day before waivers (waivers applied at the end of quarter).

Contact your instructor if you have any issues with the due date (e.g. illness, emergencies).

Remember: There will be questions on the material covered in this and future programming assignments in both quizzes and exams.

We will be testing each of the four (4) files you submit both independently and together in order to give you credit for what works. There are 30 points available as follows:

Up to 8 points (at our discretion) if the following files compile without warnings. This has to be a

meaningful attempt to write the code that meets the specifications of the program. Random C

statements, or empty files will not be awarded any points:    csv.c (3 points), setorder.c (1 points), splitrow.c (2 points), wrtrow.c (2 points)

Up to 4 points (2 points each) for passing the two (2) tests in the text fixture.

Up to 18 points for passing the gradescope tests (these will only be run after the late deadline) these will test data test and error condition handling in both main and token.

Minus 6 points for one or more uses of array indexing in your code. So there should be no use of [ ] anywhere in your code except for the variable array definitions in main() - in the file csv.c. You must only use pointers in this assignment. This means [ or ] MUST NOT appear anywhere in the code except array definitions, even comments.

Minus 1 point for each submitted file that compiles with a warning (max of 4 points deducted). Minus 2 points at our discretion, for not following the C style guidelines: CSE30 C Style Guide

Need Help?

You are always welcome to go to either Instructor or TA office hours. Their hours are listed on both the Canvas calendar and the autograder calendar. In office hours, conceptual questions will be prioritized.

If you need help while working on this PA, make sure to attend labs hours with a tutor at:

https://autograder.ucsd.edu

You can also post questions on Piazza.

https://piazza.com/ucsd/spring2024/cse30_sp24_a00/home

Assignment – Writing a Data Extraction Utility and Pointer Practice

The goals of PA4 are:

1.   To get practice writing code using C pointers, string arrays, arrays of pointers to strings, and working with basic command line arguments (argc, argv).

2.   Learn about text files whose contents are in the Comment Separated Value (CSV) format.

3.   Practice with C library routines for reading input one line at a time and converting strings to integer values.

4.   Learn to develop and test individual code modules.

5.   More practice in writing test data for a test fixture. You will need to do this as we only supply simple tests and much of your career writing software will include testing/debugging your code.

Overview of CSV File Format used in this PA

A   common   format   for   text   files   for    use   in   moving   data    between   computer   programs   is   the CSV (comma-separated values) format. Spreadsheet programs like Excel and Google sheets use CSV formatted files to import and export the table data cells with other programs. Database systems use CSV formatted files to move data as part of the ETL (Extract/Transform and Load) processing. For more information on what the ETL process is (if interested) you can start with:

https://en.wikipedia.org/wiki/Extract,_transform,_load

There are no formal standards for the format of CSV files. In the list below are the characteristics of the CSV files you will be working within this programming assignment. Please read it very carefully.

In this writeup, a descending "b" is a single blank (space) and a descending "n" is a single newline .

1.  A CSVfile stores tabular data in printable ASCII (see the table in the lecture slides).

2.  Each line (or row) of a CSV file, called a data record, is always terminated (the last character in the

row) by a single newline n.

3.  For this assignment you can assume that every row in the input data is properly terminated with a single newline.

4.   There  are no restrictions on the number of characters in a row. Caution: You need to be aware that a row may be truncated (the newline is missing) by your program due to buffer size limits when reading the row (discussed later in this writeup).

5.   Rows in the input are numbered from the top of the file to the bottom of the file where the first row in the file is row number 1.

6.  A  valid  CSV  data   record  consists  of two or   more data  fields (or columns)  separated   by  a  single delimiter.

7.   Columns in each  row are numbered from left to right in the row. The first column in the row is column number 1.

8.   There are two types of delimiters that are used to separate columns in a row:

a.  A single comma , character that separates two adjacent columns in a row.

b.  A single newline n character. A newline in a CSV file has two (2) uses. It signifies both the end of the last column (rightmost column) in a row and the end of a row.

9.   The data in a column starts with the first character right after the delimiter to the left and ends with the last character right before the delimiter to the right. For column 1, column data starts with the first

character in the row and ends with the last character right before the delimiter to the right. 10. The first row of a CSV file is the required header row which has two uses:

a.   A  description  of what  data can be found in each column (how to interpret the data). In other words, the  header  row  is  a  guide  on  how  to  use  data  in  each  column.  You  do  not  need to consider how to use column data (including the header row) in this PA.

b.   The  number  of columns in the  header row also specifies the number of columns in the rows that follow it. For example here is a header row that contains three (3) columns. After reading the header row, we now know that each valid row in this CSV file always contains only three (3) columns.

student bname,student bid,graden

11. Assume that the first  row  in a CSV file is always the header row regardless of the contents (if any) in each column.

12. A row must contain 2 or more columns. Caution: You need to be aware that in this PA there is an upper limit on the number of columns in a row (including the header row) that needs to be handled.

13. Rows that do not contain the same number of columns as the header row are considered to be in error and are dropped (no output) during processing operations on the CSV file.

14. An  empty  column  is  specified  by  adjacent  delimiters  with  no  data  between  them.  In  the following example, we have a row with three (3) columns, column 2 is empty (it has no data):

SambStudent,,85n

A valid row can have all columns empty (though this row is useless) (the descending n is a newline).

,,n

15. Any character can appear as part of the column data area except for a delimiter. All characters inside a column (including empty columns) are preserved including the number and position of blanks (examples below) during processing by your program. There are no escape sequences in row data. In the following example we show several variations of valid three (3) column rows. Observe that some include empty columns.

aaa,MMM,ccc n

zzz,123,ccc n

zz bbz,X bbbbZ,bcccbn

zzz,123,n

zzz,,n

,,n

aa,bbb,cc n

Operational Overview of the Program you will write

In this programming assignment you will write a program called csv that performs simple data extraction and formatting processing of CSV formatted data.

The program is invoked as follows with one or more arguments.

./csv column-number [...]


The program reads CSV formatted data from standard input, one row at a time (including the header row). It then extracts specific columns from the input (as specified in the command line arguments).

Next, based on the command line arguments, the program creates a new CSV row consisting of only the extracted columns from the input row and arranges them in the order they appear on the command line (left to right) (be aware selected columns can appear more than once). Caution: In this PA, your program will limit the number of output columns due the array size limits.

Next, it writes this new row consisting of the extracted columns in CSV formatted data to standard output one row at a time (including the header row).

Finally it prints a summary (to stderr) of the total number of rows read from the input and the total number of rows that were not included in the output.

An input row is dropped during processing if the row has an error (wrong number of columns, missing newline) or the writing of the row resulted in an error.

The drop count is the number of input rows missing from the output due to errors.

Here is an example of how this program is executed on the command line.

./csv 3 1 4

After reading the first header row (and before outputting it), we can calculate how many columns are present in valid rows as it is the number of columns found in the header row. Rows after the header row that do not contain exactly the same number of columns as in the header rows are not output.

If the header row is missing (empty input), the program returns with EXIT_SUCCESS (there is nothing to do and no error in processing has occurred).

If the header rows contain less than 2 columns an error message is printed (described later) and the program returns with EXIT_FAILURE.

The program next checks that the arguments (the list of output columns in order of printing from left to right) are valid.

1.   There must be at least one (1) argument.

2.   Each argument must be between 1 and the number of columns found in the header row. In the

example above, the valid range for the arguments is between 1 and 4 (as you can only specify up to the number of columns in a row).

3.   It stores this columnselection and output order in the output offset array (described later) for use when outputting rows.

Only after the above checks pass will the program proceed to processing rows (including the header row).

Each row is checked to make sure that it contains the same number of columns as the header row.

If the number of columns in any row (after the header row obviously) does not contain the same number of  columns contained in the header row or the row was truncated by fgets() (the ending newline is missing) the following occurs:

1.   An error message is written (described later).

2.   The count of dropped rows (rows that were invalid but not output) is incremented by 1. 3.   The row is not output (the row is "dropped").

4.   processing resuming with the next input row.

The columns specified in the input record are written to the output (in CSV format) in the order in which they appear in the argument list.

If the output fails (extremely rare) then:

1.   An error message is written (described later).

2.   The count of dropped rows is incremented by 1.

3.   Further processing of input rows stops.

4.   Summary processing (described below) is performed.

5.   The program returns.

When called as:            ./csv 3 1 4

Only columns 3, 1, and 4 in the input rows are written to the output in that order. Observe that input column 2 is not present in the output. The mapping of input columns to output columns is shown in the table below.


After all rows have been processed (or the output of a row failed), the summary processing writes to stderr the number of rows read (including the header row) and the number of rows dropped.

A dropped row is an input row that was not processed properly either due being damaged (incorrect column count or a missing \n at the end of the row) or the write to the output failed. So the data in that input row is  not in the output, so the input row data was "dropped" (missing in the output).

When the number of rows dropped is 0, the program returns EXIT_SUCCESS, otherwise (dropped is > 0) it returns EXIT_FAILURE.

Each row in the table below describes one input row and the corresponding output row in the order in which they are processed when running:

cs30sp24@pi-cluster-153:~% ./csv 3 1 4

The first row in the table is the required header row and only after reading it do we know that a valid row must contain exactly 4 columns. A blank output means the input record has an incorrect column count and was not output.


Get the Starter files from Github and the setup

Step 1: You need to get the starter files for PA4. Login into your account on the pi-cluster and run the following command. You will develop the code on the pi-cluster.

cs30sp24@pi-cluster-153:~% cd ~/cse30

cs30sp24@pi-cluster-153:~/cse30 % git clone https://github.com/cse30-sp24/pa4-starter.git pa4

cs30sp24@pi-cluster-153:~/cse30 % cd pa4

In the repository you will find the following files:

1. Makefile for compiling your program. You should examine the file so you understand how it works. The  Makefile is very similar to the one used in PA3. Running make will recompile your program, and running make test will run the test program runtest on the data files listed on the LIST = line.

2. csv.c when completed this file will be graded. It is a starter file where your main routine is located in

this PA. There is some small amount of code to get you started. The comments in the file will guide you  what needs to be written and at what place in the file. You will need to edit this file to complete this PA.

3. splitrow.c when completed this file will be graded. It is a starter file where you define your implementation of the function splitrow().

4. splitrow.h It is the public interface file for the function splitrow(). You should not change this file.

5. setorder.c when completed this file will be graded. It is a starter file where you define your implementation of the function setorder().

6. setorder.h It is the public interface file for the function setorder(). You should not change this file.

7. wrtrow.c when completed this file will be graded. It is a starter file where you define your implementation of the function wrtrow().

8. wrtrow.h It is the public interface file for the function wrtrow(). You should not change this file.

9. libpa4.a Is a library containing working versions of the four (4) files you need to implement. So it has   csv.o, splitrow.o, setorder.o and wrtrow.o. using version.h you can choose to use the file in libpa4.a or not.

10. version.h This is a file that you will be editing during your program development. When make is run    the contents of this file will determine whether to use your version of  csv.c, splitrow.c, setorder.c or     wrtrow.c when creating the executable csv or to use the supplied working version of those files stored as compiled .o files in the library libpa4.a.

11. runtestis a shell program for testing your program. It is basically the same one as in PA3 except it is

used a bit differently. This is described later.  Make sure you do a chmod 0755 runtest before you use it (so you can run it).

12. in is a directory where the test input files are located. The starter code includes two (2)  basic tests to use as guidelines. You will need to add your own. There are two types of files here, a cmd file (.csv argos for a specific test) and a data file (input for the corresponding cmd file. So test 1 consists of two   files:  a cmd1 file and a data1 file. This allows you to not only control the data used in each test, but try different numbers of command line arguments.

13. exp is a directory where the expected files for the output checking (both standard out and standard error) are located.

14. out is a directory where your program output (omit) is put by  runtest.

Step 2: Make sure all the executable shell scripts have the proper execute bits set (see the manual for chmod, % man chmod for more information).

cs30fsp24@pi-cluster-153:~/cse30/pa4 % chmod 0755 runtest in/cmd*

Working with functional specifications - Motivation

It is common practice in industry to be given a function interface and operation specification and be required to implement the specification for integration with either existing code or with code being written by your co-workers. You have to be very careful that you perform all the operations as described in the specification as it may cause other code to fail to operate properly. You may NOT change or add to any aspect of the specified operations, arguments (including position, types and number of arguments), error messages to print (if any) under specific situations, and the various specified return values from your function.

In this PA you will write four functions: main(),splitrow(),wrtrow(), setorder(). The specification for each of these will be found in the comments at the top of the files csv.c, splitrow.c, wrtrow.c and setorder.c.

Only certain functions output error messages. The exact fprintf() formats are included in the starter files for    those functions (they appear as comments). Make sure you use them all and do not change the format string (it is ok to use any variable names you like).

fgets() reads just one line of text on each call. A line is defined to be all the characters on the input stream up   to and including the first newline it sees each time it is called. It then stores the line into an array of characters (buf) as a string (it always adds a ‘\0’ after the last character). fgets() returns NULL when it detects EOF on the  input stream (there are no valid characters in the buffer).

char *fgets(char *buf, int n, FILE *stream);

In the function prototype above, buf contains the starting address of a character array whose size is n elements, and stream is the stream to read from (like stdin).

A loop to read the input (stdin in this example) one line at a time until EOF is detected (fgets() return NULL when EOF is detected). A fgets() loop until EOF is detected looks like this:

while (fgets(buf, BUFSZ, stdin) != NULL) {

// your code

}

In the code snippet above, fgets() reads up to BUFSZ-1 characters into the character array pointed at by buf each time fgets() is called.

fgets() will always fill the buffer to no more than BUFSZ-1 characters from the input, throwing away any characters that exceed that limit. fgets() always adds  a ‘\0’ termination after the last input character that it places into buf. This ensures that the buf array always contains a valid string.

Below is a proper csv row with two columns in the array pointed at by buf. The picture shows the contents of the character array as it would look after being filled by a call to fgets() when BUFSZ is 15 (char buf[15]).

The first column is cse! and the second column is Line22. Notice that the buffer contains both the newline ('\n') row termination followed by a '\0'added by fgets() to terminate the string.

You will always get a string from fgets(), but if there is not enough space in the buffer, fgets() truncates the input to contain no more than BUFSZ-1 characters. Since all proper rows must end in a '\n', a missing'\n' indicates a flawed row. This is most likely caused by fgets() truncating the row whose length was greater than BUFSZ-1.

fgets() has some severe limitations that you need to be aware of. What would happen if BUFSZ is 10, (char buf[10]) and fgets() reads the same 12 character input row as in the prior example above?

fgets() will truncate the input at 9 characters (BUFSZ-1) and then add the'\0' at the end. The buffer now contains a flawed record that should be dropped (not output). This is because the row does not end in a newline ('\n') and you have no idea how many characters were dropped from the row at this point, you must assume the data is corrupt, and should not be used.  This is shown below.

Continuing the example above, the next call to fgets() would return the remaining characters (shown below). While this has a newline in it, it has only one column. fgets()  would cause one input row to look like two, but given the limitations offgets() this is all what we want you to do in this pa.

So what do you do?

The function splitrow() in this PA has the task of detecting incorrect rows such as: wrong column count, or the end of record'\n'is missing and returning the flawed indication to main(). splitrow() will be called twice, and   just let splitrow() reject the buffers passed to it. This will impact code in csv (described in detail in another section,to mis-count input lines and dropped lines) but this is ok for this PA.

While it is possible to have a flawed input with a very long row that through truncation creates a row with the correct number of columns, you can assume that this kind of test case will not happen as the code to handle   this is somewhat complex and not the point of this PA (to practice pointers).

Implementation Notes - using strtol()

strtol() is a C library routine that converts character strings into long integers. We will use this function to convert each command line argument string in argv, to integer column numbers. strtol() will be used in     setorder() to process the command line arguments.

long int strtol(char *str, char **endptr, int base)

Returns the stringstr converted to a long int.

str is a pointer to the string to convert

endptr address of a char pointer variable (strtol() will change the contents of this variable) base number base (base 10 in our use)

The string str is only allowed to contain a positive number (>= 0) in ASCII with no extraneous characters. If the   string str is not a valid positive number or has extra characters in it, *enptr will contain the address of the first

character found in str that is in error (not a number).All you need to do is to check the string str is ok is the following check:

If ( *endptr == '\0'     // rest of checks

strtol() also sets a global variable, errno, to a non-zero value only when there are other conversion errors.    strtol() can be difficult to use as it uses an output variable (endptr) and changes the value  stored in errno only when it sees an error (so you must set errno to 0 before each use). Here is an example of how to use strtol() correctly.

Make sure you always set errno to 0 directly above each time in your code that your call strtol().

#include

// interface for strtol() and other library functions

#include

// interface for using the global variable errno

char buf[] = "33";

// test, this would be an argv element in your code

char *endptr;

// pointer variable that strtol() will change

int number;

// the converted number

errno = 0;

// set errno to zero before each use of strtol()

number = (int)strtol(buf, &endptr, 10);     // cast the long value to int

Here is a sample that checks that the conversion was completed without error. This code snippet would appear right after a call to strtol() such as shown above.  In this sample we also check that the value returned by

strtol() (which is stored in the variable number)  is  > 1. You will need to make similar proper value checks in setorder().

if ((errno != 0) || (*endptr != '\0') || (number <= 1)) {

fprintf(stderr, "some error message\n");

// rest of your code

} else {

// number contains a valid value, rest of your code

}