CS计算机代考程序代写 data structure c/c++ compiler Excel ECE 209 Program 3: albums Summer 2021

ECE 209 Program 3: albums Summer 2021
Due Saturday, July 31 @ 11:59pm
This programming assignment must be completed individually. Do not share your code with or receive code from any other student. Evidence of copying or other unauthorized collaboration will be investigated as a potential academic integrity violation. The minimum penalty for cheating on a programming assignment is a grade of -100 on the assignment. If you are tempted to copy because you’re running late, or don’t know what you’re doing, you will be better off missing the assignment and taking a zero. Providing your code to someone is cheating, just as much as copying someone else’s work.
DO NOT copy code from the Internet, or use programs found online or in textbooks as a “starting point” for your code. Your job is to design and write this program from scratch, on your own. Evidence of using external code from any source will be investigated as a potential academic integrity violation.
For this assignment, you will be reading and searching through information about music albums. The learning objectives of the program are:
• Write C functions according to specifications.
• Use standard library functions for text file I/O.
• Manipulating data using linked lists.
Background
Before the age of digital streaming, music was released in “albums.” An album is a collection of songs packaged in a physical medium, like CDs or vinyl disks. (Yes, I know that albums still exist.) In 2012, Rolling Stone (there used to be these things called “magazines”…) released a ranking of the top 500 albums of all time. This assignment uses that data, and other collections with the same format.
The job of your program is to (a) read a file containing information about ranked albums, and (b) allow the user to extract interesting subsets of that information. The primary data structure used in the program is a linked list of albums, ordered by rank.
Main Program
You are provided with a program in main.c. This program should look very familiar. It’s simply the user interface that will allow you to interact with the file data. The user types a command, followed by additional information required by that command, and the program prints a result.
Command Argument Description
read
file name
Reads the album information from the file. Reports how many albums were read.
1

all
none
Prints all albums in the most-recently-read file. Printed in rank order, from highest (lowest rank number, e.g., 1) to lowest (highest rank number, e.g., 500).
rank
number
Prints the album (if any) with the specified ranking. There will be at most one album with a matching rank. Printed in rank order.
year
number
Prints all the albums (if any) that were released in the specified year. There may be multiple albums with the same year. Printed in rank order.
artist
one-word string
Prints all the albums whose artists contain the specified string. There may be a partial match (e.g., “Bo” matches both “Bob Dylan” and “Bonnie Raitt”). There may be multiple albums that match. Printed in rank order.
genre
one-word string
Prints all the albums whose genres include the specified string. The matching is for the whole word (e.g., “blue” does not match “blues”). There may be multiple albums that match. Printed in rank order.
quit
none
Ends the program.
Program Structure
The main program is implemented in main.c. This is provided for you. You are also given three header files: csv.h, Album.h, and AlbumList.h. You must create three source code files that define the functions declared in the header files:
csv.c — function for reading from a CSV file. Album.c — functions related to a single album AlbumList.c — functions related to lists of albums
All of these files are compiled and linked together to create a single executable program. See the Appendix for information on how to build a project with multiple files. You should start by creating dummy versions of all the functions, so that you can have a working program as you work incrementally.
File Format: CSV
We are using text files1 in this assignment. We use a more-or-less-standard CSV (comma-separated values) format that is a common way to store a spreadsheet in a text-only format. Microsoft Excel can read and write CSV files. Typically, an extension of .csv is used in the name of a CSV file.
Details about CSV can be found here: https://en.wikipedia.org/wiki/Comma-separated_values
The idea is that values are stored in text format. A line of text corresponds to one row of a spreadsheet, or one record of related data. Data items on a single row are separated by a comma. Spaces are not ignored — if there’s a space, it is considered to be part of the value. A linefeed character indicates the end of the last data item on a row. Each row should have the same number of data items, but that’s probably not enforced. (In our files, it will be true.)
1 This means you will use “r” mode and fscanf. Do not use “rb” or fread. 2

Example: Here are two rows of integer data in CSV format, with four data items in each row.
10,100,35,19 -97,62,800,-1234
Example: Here are two rows of string data in CSV format, with four data items in each row.
fee,fie,foe,fum
tweedle-dee,tweedle-dum,hansel and gretel,Snow White & 7 dwarves
Things get a little tricky when the data contains a comma. In that case, the data item uses quotation marks to mark the beginning and end of the item. The quotation marks are NOT part of the data.
Example: String and integer data, some containing commas. The second example is all in one line, with no linefeed, even though
1966,Sergio Leone,”The Good, the Bad and the Ugly” 1998,”Joel Coen, Ethan Coen”,The Big Lebowski
Double quotes can be used for any field, but they must be used when the field contains a comma or a double-quote character. Double-quote characters within a field must be represented by two double- quote characters.
Example:
1997,Ford,E350,”Super, “”luxurious”” truck”
Album File Format
The data for this program has six fields in each row of the CSV file, in the following order:
• Number (integer)
• Year (integer)
• Title (string)
• Artist (string)
• Genre (string)
• Subgenre (string)
The first row of the table contains column header strings. You must read them and ignore them. Some of the strings are long. You can assume that no string will be longer than 200 characters.
CSV functions
There are two functions declared in csv.h. You must define both of these functions in csv.c. Each function reads the next field from a CSV file. One expects the field to be a string, and the other expects the field to be a decimal integer.
int getCSVString(char * str, FILE* fp); int getCSVInt(int * num, FILE* fp);
Both functions return 1 if the read is successful, and 0 otherwise. Because we are reading one field at a time, the functions do not know whether the field should end in a comma or end in a linefeed. In either case, the ending character (comma or linefeed) must be read and ignored, so that the file pointer is positioned at the beginning of the next field.
3

Hint: Implement and test these functions first. Write a test program that opens a file and reads CSV fields. See the Appendix to learn how to add multiple executables to your project. This will allow you to write test programs for parts of your code, and still use the main program when you want to test that way.
Hint: When reading string data, your code will be simpler if you read one character at a time. If you try to use %s, it will be more challenging to deal with the terminating comma or linefeed, with spaces in the field data, and with quoted data.
The Album type
Album.h has a struct definition for data that represents an album.
struct album {
char title[150];
char artist[150]; unsigned int rank; unsigned int year; unsigned int genre;
};
Note that there is no subgenre field. The subgenre information from the album file will be ignored. (But you still need to read it!)
There is also a typedef that creates the alias Album to mean struct album.
Also note that the genre is an integer. Each genre is assigned an integer value, to save space. Because an album may have multiple genres, we use a bit field as the representation. Each genre is a separate bit, and we can OR the codes together to represent multiple genres. Each genre code has a macro to give it a symbolic name. (For information about macros, see the video and notes on the Moodle site in the “Macros and File I/O” section.)
Example: If an album is both Rock and Blues, its genre code is BLUES | ROCK.
Each genre also has a corresponding string that is used for printing the album. There is a macro for each
string, such as ROCK_STR.
Album functions
The following functions are declared in Album.h and must be defined in Album.c. char * genreToString(unsigned int g, char *str);
Given a genre code, write the corresponding genre string to str. The caller is responsible for making sure that str points to an array with enough space to hold the string. If more than one genre bit is set,
then the string concatenates all of the corresponding strings in alphabetical order, separated by one comma and one space. See the examples in the table below:
// official name of the album
// artist who recorded the album
// rank in the Rolling Stone list
// year of release
// primary genre — see encoding below
Genre Code
BLUES
ROCK | POP | JAZZ ELECTRONIC | FUNK_SOUL
Decimal (Hex)
1 (0x001) 1344 (0x540) 20 (0x014)
Genre String
Blues
Jazz, Pop, Rock Electronic, Funk / Soul
4

The return value is a pointer to the string. When there is no error, the return value will be the same as the pointer passed in. If there is an error, NULL is returned. (An error occurs if the genre code includes bits that do not correspond to the standard genre codes.)
Hint: The genre codes are assigned in alphabetical order. So if you check the bits from least significant to most significant, you can easily compose the string by concatenating the corresponding strings.
unsigned int stringToGenre(const char *str);
Given a string, match it to the corresponding genre code and return that code. The string may contain multiple genres. If so, the genres will be separated by a single comma and a single space. Each part of the string must exactly match one of the genre strings defined in Album.h.
If there is an error in the string, the return value must be 0.
Album * newAlbumFromFile(FILE* fp);
Allocate a new Album (using malloc). Read the album information from the file provided. Assume that the file pointer is located at the beginning of an album record. If there is no error during the read, return a pointer to the newly-created struct. If there is an error, return NULL.
Use the CSV functions to read the data. Read two integer fields, followed by four string fields. (You must read the subgenre field, even though you won’t save it; otherwise, it will mess up the read for the next album.)
void printAlbum(const Album *a);
Print the album information to standard output. Each album will be printed on two lines. The first contains the rank and the album title. The second line contains the artist, year, and genre. Follow the following format exactly:
#50: Here’s Little Richard
>> Little Richard (1957) :: Blues, Rock
Print a linefeed at the end of each line. Do not print extra spaces.
Linked list of albums
AlbumList.h defines struct album_node, which can be used to build a linked list of albums. It also has a typedef that creates an alias AlbumNode that means struct album_node.
struct album_node { Album* album;
struct album_node *next; };
Note that the data field (album) is a pointer, not a struct. This means the album must be allocated separately. In the functions below, you are given an album pointer to add, so your function does not need to worry about how/where the album was allocated — it’s already been taken care of.
When you have a linked list of albums, it will look like the figure on the top of the next page.
There are also a number of functions declared that manipulate the list of albums. Read each specification carefully!
int readAlbumFile(const char *filename);
Given a file name, this function reads the album information from the file. (1) Create an empty list to hold the albums. (2) Open the TEXT file for reading. (3) Skip the row that contains the column header
5

strings. (4) Use newAlbumFromFile to read the next album info and create a new album structure. (5) Add that new album to your list of albums. It is strongly recommended that you insert the album in the proper place, so that the resulting list is always sorted by rank. (6) Go back to (4) until there are no more albums to read. Finally, save your new albums list to a global variable. (What????) This will be the list used for all the other functions.
For step(5), use the insertAlbum function described below.
If there is an error, return 0. Otherwise, return the number of albums that were read.
const AlbumNode *allAlbums();
This returns a list of all albums read from the most recent file, sorted in rank order. (If you ordered the list as you created it, this is simply a matter of returning the list, which you saved in a global (!!!) variable.) The const just means that the caller will not be able to modify the list that you return.
AlbumNode * insertAlbum(Album *a, AlbumNode *list);
Inserts a new album (a) into an existing list (list), in rank order. Return a pointer to the head of the new list. Note that the initial list might be empty. Rank order means increasing order of rank numbers.
void printAlbums(const AlbumNode *list);
Given an albums list, print them. Use printAlbum for each item of the list. const Album * findRank(unsigned int rank);
Search the list for an album with the specified rank. Return a pointer to that album. If no album with the rank is found, return NULL.
AlbumNode * findYear(unsigned int year);
Search the list for albums with the specified year. Return a list of all such albums, which might be empty. The list must be in rank order.
AlbumNode * findArtist(const char * match);
Search the list for albums with an artist that match the string provided by the caller. The match can be partial and must be case-insensitive. The returned list (which could be empty) must be in rank order.
Examples: “Bob” matches “Bob Dylan” and “Bob Marley”. “bo” matches “Bob Dylan”, “Bob Marley”, “Bonnie Raitt”, “Jumbo the Elephant”.
AlbumNode * findGenre(const char * genreWord);
Search the list for albums with a genre that matches the string provided by the caller. The match is case- sensitive, but must match an entire word in the genre string. The returned list (which could be empty) must be in rank order.
6

Examples: “funk” matches “Funk / Soul”. “electro” does NOT match “Electronic”. “funky” does NOT match “Funk / Soul”.
Hint: First find which standard genre string matches the input string. There will only be one such match, or none. Then use the corresponding genre code (integer) to match against the genres of the albums in the list.
Hints and Suggestions
• Don’t overcomplicate the program. Do not use anything that we have not covered in class.
• The strstr function in the string.h library will be helpful for doing partial string matching. Convert both strings to lower-case before doing the match. (But don’t change the string that is stored as part of the album data — that will affect how the album gets printed.)
• For compiler errors, look at the source code statement mentioned in the error. Try to figure out what the error is telling you. Try to fix the first error first, and then recompile. Sometimes, fixing the first error will make all the other errors go away. (Because the compiler got confused after the first error.)
• Use a source-level debugger to step through the program if the program behavior is not correct. If you are using CLion on your own computer, the debugger is integrated with the editor and compiler, so there’s no excuse for not using it.
• For general questions or clarifications, use the Piazza, so that other students can see your question and the answer. For code-specific questions, post a private message to Piazza and attach your code as a file. (Do not copy and paste!)
Administrative Info
Updates or clarifications on Piazza:
Any corrections or clarifications to this program spec will be posted on Piazza. It is important that you read these postings, so that your program will match the updated specification.
What to turn in:
• Submit your csv.c, Album.c, and AlbumNode.c files to the zyBook assignment “Summer 2021 – Program 3 – albums”.
Grading criteria:
10 points:
10 points: 10 points: 5 points: 10 points:
Proper coding style, comments, and headers. No global variables, except as needed. (In this case, “the spec requires it” is a good justification for using a global variable.) No goto. See the Programming Assignments section on Moodle for more style guidelines. (You will not get these points if you only submit trivial code.)
CSV functions.
genreToString and stringToGenre. printAlbum.
readAlbumFromFile.
7

10 points: 10 points:
5 points: 5 points: 10 points: 10 points: 5 points:
insertAlbum (do this before readAlbumFile — you’ll need it).
readAlbumFile and allAlbums (must be tested together, because we otherwise have no access to the list of albums created by the read function).
printAlbums. findRank. findYear. findArtist. findGenre.
NOTE: Points may be deducted for errors, even if all of the zyBook tests pass. This can happen if your dummy function “accidentally” passes a test, even though it doesn’t do anything. It may also happen if it is obvious to the grader that the program is written specifically to pass the specific tests, and would not pass other similar tests.
Appendix A: Setting up the CLion project
1. Create a new project for a C executable with the C11 standard.
2. Download the main.c file (overwriting the main.c created by your project).
3. Download all the .h files into the same directory.
4. Download the .csv files into the same directory.
5. For each additional file (csv.c, Album.c, AlbumList.c), do the following:
a. Right-click on the project name.
b. Select New > C/C++ Source File.
c. In the menu, set the file name (e.g., csv) and select the .c file. Check the box next to “add to targets”. Then click OK.
If you don’t add to the target, then you will need to edit the CMakeLists.txt file to include the .c files in the list of files to be compiled for the target executable:
add_executable(album_test main.c csv.c Album.c AlbumList.c)
Each file is compiled separately. Each file should at least include the corresponding .h file. For example, in csv.c, you will need:
#include “csv.h”
You may need to include other header files. Include the header files for the functions you need to use — do NOT copy the declarations of those functions into your file.
You will need to set the Working Directory, as you did in Program 2, so that the program can file the .csv files.
Also, set the C compiler flags to be consistent with the zyBook:
set(CMAKE_C_FLAGS “-Wall -Werror”)
8

Appendix B: Adding a test program
CLion allows you to have multiple executables in a single project. This lets you create a specific test program (or programs) to test your functions instead of relying on main.c.
1. Right-click on the project name.
2. Select New -> C/C++ Source File.
3. Name the file and give it the .c type. DO NOT check the “add to targets” box. We’re going to create a new target, not add this file to the other target.
4. Edit the CMakeLists.txt file to add your new executable. List all of the .c files whose functions you need. For example, let’s assume you create a test program for the csv function, and you called your file csv_test.c:
(add-executable csv_test csv_test.c csv.c)
When you reload the CMakeLists.txt file, you will find csv_test as a choice in the pull-down menu at the top right of your CLion window. If you need to open a file, you will need to do Edit Configurations to set the Working Directory for this program. (You will need to do this every time you have a new executable that needs to open file.)
To compile/run your test program, select it in the drop-down menu. The click Build or Run or Debug, as you normally would.
9