Each exercise this quarter is rated on a integer scale of 1 – 5, inclusive, with 1 being the "least time-consuming" and 5 being the "most time-consuming".
This difficulty scale is meant as a rough guide for you in predicting the amount of time to set aside for each exercise as you balance the work required for 333 with your other obligations. However, it is necessarily imperfect as everyone's set of circumstances and experiences with the exercises differ. If your experience with an exercise does not align with its rating, that is not a reflection of you or your abilities.
Write a C++ program that reads a text file whose name is given as a single command-line argument, counts how many times each string appears in the file, and then prints every string alongside its count in sorted order. For this exercise, a "string" is any run of non-whitespace characters, and two strings count as the same only if they match exactly, including capitalization.
Reading the file is the interesting part: rather than reading strings directly, you must factor the read-and-parse step out into a helper template function with exactly this prototype:
template <typename T> bool ReadValue(ifstream& in, T* const output);
Each call to ReadValue reads a single token from the
input stream in, attempts to parse that token as a
value of type T, and stores the result in
*output. It returns true on success and
false if either the read or the parse fails (for
example, a conversion error or reaching EOF). Your
main should count strings by repeatedly calling
ReadValue<string> until it returns
false.
Print one string per line, sorted using the ordinary string
ordering (operator<). Each line is the string,
followed by a single space, followed by its count, with no other
whitespace before the string or after the count.
'\n'). Most text editors add a trailing
newline for you when you save, so we include
nonewline.txt as a file that does not end in one. Make
sure you handle it: don't count a phantom empty string, and don't
double-count the last one.
The short examples below use trimmed-down inputs so they are easy to follow. The files in the starter code are longer, but the behavior is the same.
First, counting strings. If you run ./ex6 words.txt
where words.txt contains:
cat dog cat bird cat dog
the program prints:
bird 1 cat 3 dog 2
The same counting logic also works for other types. Reading
ints from a file that contains:
3 1 3 2 3
would print:
1 1 2 1 3 3
We come back to how that second version works in the
Implementation Notes; the only thing that
changes is the type you hand to ReadValue.
The starter code gives you quotes.txt, nonewline.txt, and numbers.txt so you can try the real inputs right away.
You do not need to do any extra processing of the input, such as
stripping whitespace or punctuation; the stream extraction operator
(>>) already splits on whitespace and does the
parsing for you. To distinguish a clean end-of-file from a real
error, investigate the stream's good(),
bad(), and eof() methods. More
information on reading files in C++
.
Good template code assumes as little as it can about the type it
gets instantiated with. Here, the only things you should count on
are that T has a default constructor and an
operator>> that reads from an input stream.
Don't reach for anything that only makes sense for a
string.
That is exactly what the int example above is
showing. If main declared int value; and
called ReadValue<int>(infile, &value) into a
map<int, size_t>, the same
ReadValue would count integers instead of words. The
template body never changes; only main does. That is
a handy way to sanity-check your own code: if it still works after
swapping in int, you know you didn't hard-code
string anywhere.
main for one that uses
ReadValue<int> to count integers, so a template
that assumes string will fail here. Just remember that
the main you actually submit still needs to read
strings.
This program takes input from both the command line and a file,
and either can fail: a missing argument, a filename that can't be
opened, or a stream error mid-read. Check for these, and on an
unrecoverable error print a useful message to standard error and
return EXIT_FAILURE from main. For this
exercise you may treat a parsing error as unrecoverable.
Submit your work by creating
an ex6-submit tag in your
exercise repo before the assignment deadline. Your code must
live in ex6/ex6.cc, matching that path and
capitalization exactly. Any other files in the ex6
folder are ignored, so you may leave extra files there.
For full credit, your code must:
attu, or CSE
home VM) with the command:
$ g++ -Wall -g -std=c++17 -o ex6 ex6.cc
g++ and valgrind)..cc file with your name(s) and CSE or UW email
address(es).cpplint.py).