Backward file reader
Learning goals
You will learn or practice how to:
- Program with files.
- Program with structs to encapsulate data.
- Buffer data read from a file to improve program performance.
- Apply test-driven development (TDD).
Overview
You will write code to read files in backwards. You will need to design your own struct
to fullfill the requirements of this homework. See the Requirements table below for more detail on what you will create.
Background information
A common use of a backwards file reader is to display recent events from the log file of a website. These files can be very large and difficult to inspect by hand, but reading the file backwards to forwards allows for more easy processing of logs. In this assignment you will implement such a reader to read files backwards.
Generally, file I/O is a very expensive operation in any program and can be the bottle neck which prevents better performance. In this assignment, you will help to alleviate performance issues by buffering file I/O operations into a custom structure, and then use that structure to read in a file backwards and display it line by line.
For this assignment you will need to use several new functions for interacting with files. A complete description of these functions can be found by typing man function_name
but here is a brief description of each:
FILE* fopen(const char* filename, const char* mode)
- This function opens the file at filename with mode mode and returns a FILE struct which you can use to interact with the file. The modes you may need for this assignment are as follows:- w - used to open a file for writing, this mode deletes a file's contents if it has any or creates a new file
- r - used to open a file for reading
int fclose(FILE* stream)
- This function is used to close a file you have opened withfopen
so that all resources relating to the file are freed. You must have exactly one call tofclose
for each call tofopen
.int fseek(FILE* stream, long offset, int whence)
- This function moves where in the file you are currently reading from/writing to. Stream is the FILE structure you obtained fromfopen
, offset is the distance to move (can be positive, negative, or zero) and whence is a constant describing where you should move relative to. Whence can be one of three constant values:SEEK_SET
- seek to an absolute position from the beginning of the fileSEEK_CUR
- seek from the current location in the fileSEEK_END
- seek from the end of the file
long ftell(FILE* stream)
- This function returns your current position in the file measured from the start of the file.size_t fread(void* dest, size_t size, size_t count, FILE* stream)
- This function reads in count elements of size size from stream and copies them into the buffer provided to dest. For example if you wanted to read in 10 ints from a file you might do the following:FILE* fp = fopen("ints_data.txt", "r"); int *array = malloc(sizeof(*array) * 10); fread(array, sizeof(*array), 10, fp); fclose(fp);
Warm-up exercise
This assignment includes a warm-up exercise to help you get ready. This accounts for 10% of your score for HW10. Scoring will be relatively light, but the usual base requirements apply.
The structure of the warmup.c file is described in the Requirements table below. You should write your own warmup.c.
Opt out.
In a hurry, and don't need the practice? This warm-up is here to help you learn what you need to succeed on the rest of this assignment—not to add additional work. Therefore, we give you an option. Those who feel that they do not need the practice may "opt out". by modifying warmup.c so that it does nothing but print the following message exactly and then exit:
I already know this and do not need to practice.
If you do that, then your score for HW10 will be based solely on the rest of this assignment. If you leave the warmup.c undone, if you do not turn in a warmup.c, or if the message it prints does not match perfectly, then you will receive 0 for the warmup portion of this assignment (10%).
Doing the assignment
Use 264get HW10
to fetch the starter code.
Use test-driven development to complete this assignment incrementally.
Requirements
- Your submission must contain each of the following files, as specified:
file contents backwards_file.h types FileWrapper
Astruct
to hold all information you will need in order to read a file backwards and buffer a specific amount.- You may include whatever fields you think are necessary to complete this assignment.
- Hint: You will want to store a file pointer to the open file.
You may add additional types and/or constants to backwards_file.h, if you wish. backwards_file.c functions create file wrapper(char✶ filename, char✶✶ a error)
→ return type: FileWrapper ✶Open the file and create aFileWrapper
with the information that will be needed byread_line(…)
.- OPTIONAL: Return
NULL
in case of an error (e.g., unable to open file).
read line(FileWrapper✶ fw, char✶✶ a error)
→ return type: unsigned char ✶Return the next line from the back of the file.- You will allocate the required memory for the string you read from the line.
- You may assume the caller is responsibible for freeing the returned line.
- Return
NULL
ifyou failed to open the filethere was an error reading the file. - When a line ends with a '\n', include it in the string that is returned. For the last line in a file that doesn't end in '\n', return as is, without the '\n'.
- The characters within each line should be in the same order they were in the file.
- The behavior is similar in spirit to the
tac
command in bash. You might find it useful to try that command or read its man page. - OPTIONAL: After all lines of the file have been returned,
any subsequent calls to
read_line(…)
should return NULL. To distinguish from file errors, set*a_error = NULL
.
free wrapper(FileWrapper✶ fw)
→ return type: voidFree all heap allocated memory assoicated with theFileWrapper
and close the file pointer.test_backwards_file.c functions main(int argc, char✶ argv[])
→ return type: int- This is the same as in previous assignments.
- Tests must result in 100% code coverage.
expected.txt test output - This is the same as in previous assignments.
- If you are using miniunit.h, you may simply redirect the output of your working tests to expected.txt to create this.
warmup.c functions print contents of file(char✶ filename)
→ return type: voidRead every byte in a file and print it tostdout
.- This should not call
malloc(…)
.
print first n chars in file(char✶ filename, int n, char✶✶ a error)
→ return type: voidRead the first n bytes in a file and print them tostdout
.- This should not call
malloc(…)
. - If there are <n bytes in the file, then print the entire contents of the file.
get first line of file(char✶ filename, char✶✶ a error)
→ return type: char✶Read the first line of a file and return it as a string on the heap.- This should call
malloc(…)
once. - Caller is responsible for freeing the memory.
- For all functions that take a parameter called
a_error
, in case of any error with the file (e.g., file not found, unreadable, etc.), set*a_error
to the error message string returned bystrerror(errno)
.- OPTIONAL: Set
*a_error = NULL
if there was no file error.
- OPTIONAL: Set
- For the main part of this homework (excluding the warmup):
- Do not read any byte more than once.
- You must read the file in fixed-size chunks.
- Pick a buffer size.
- It could be a constant in your backwards_file.h (e.g., 128 bytes)
- Alternatively, it may be dynamically determined in
create_file_wrapper(…)
(e.g., based on the file size). - Buffer size must not be 1 or the size of the file.
- When calling
fread(…)
, read exactlybuffer_size
bytes at a time. - Your last call to
fread(…)
(e.g., when reading the very beginning of the file) may read a smaller number of bytes. - Buffer size may not be <8 bytes or >1024 bytes.
- Do not read one byte at a time (unless the file is just 1-2 bytes).
- Do not read the entire file
at oncewithin a single call tocreate_file_wrapper(…)
orread_line(…)
unless the file is smaller than your buffer size.
- “line” means a sequence of bytes that begins at the beginning of a file or immediately after
a
'\n'
and ends with a'\n'
. - Make no assumptions on the size of the file or the size of the
bufferlines. - Your structure may have any fields you wish as long as it is sufficient to complete the assignment.
- OPTIONAL: You may create an alias for unsigned char like this:
typedef unsigned char uchar;
and then substituteuchar
in place ofunsigned char
in your code and the header file. -
Only the following external header files, functions, and symbols are
allowed. You may use
fputc(…)
andstdout
in either one (or both).header functions/symbols allowed in… stdbool.h bool
,true
,false
backwards_file.c
,backwards_file.h
,test_backwards_file.c
,warmup.c
string.h memcpy
,strcmp
,strlen
,strcpy
,strchr
,strrchr
,memmove
,strncat
,strerror
backwards_file.c
,test_backwards_file.c
,warmup.c
errno.h errno
backwards_file.c
,test_backwards_file.c
,warmup.c
stdio.h FILE
backwards_file.c
,backwards_file.h
,test_backwards_file.c
,warmup.c
stdio.h printf
test_backwards_file.c
,warmup.c
stdio.h stdout
,stderr
test_backwards_file.c
stdio.h fopen
,fclose
,fseek
,ftell
,fread
,feof
,ferror
,SEEK_SET
,SEEK_CUR
,SEEK_END
backwards_file.c
,test_backwards_file.c
,warmup.c
stdio.h fgetc
warmup.c
stdlib.h malloc
,free
,EXIT_SUCCESS
,EXIT_FAILURE
backwards_file.c
,test_backwards_file.c
,warmup.c
assert.h assert
backwards_file.c
,test_backwards_file.c
,warmup.c
EOF
. Feel free to ask if there is something you would like to use. - OPTIONAL: You may add const to any parameter in the header file (or anything else).
- Submissions must meet the code quality standards and the course policies on homework and academic integrity.
Submit
To submit HW10, type
264submit HW10 backwards_file.c backwards_file.h test_backwards_file.c expected.txt warmup.c miniunit.h clog.h
from inside your hw10 directory.
If your code does not depend on miniunit.h or clog.h, those may be omitted. If your tests rely on text files, submit them. In general, you should submit everything needed to compile and your code and run your tests.
Pre-tester ●
The pre-tester for HW10 has not yet been released. As soon as it is ready, this note will be changed.
How much work is this?
Q&A
Updates
3/21/2019 | Fixed warmup functions |
3/22/2019 | Removed requirement about not modifying backwards_file.h; clarified: do not use EOF ;
added warmup.c to allows symbols table in requirements |
3/23/2019 | Removed the reference to diff testing. Miniunit is preferred (but still optional). Minor clarifications. |
3/25/2019 | Specification for buffer size was expanded; deadline extended |
3/26/2019 | Corrected table of allowed functions/symbols. There is no need to use
printf(…) or stdout in backwards_file.c.
Added ferror(…) .
Submit any text files your tests depend on. |
3/28/2019 |
Added a few OPTIONAL suggestions to fill holes in the specification. Most came up in
response to questions on Piazza or in class. These will not be tested.
No bonus credit will be given.
|