## Chapter 12: Streams

## Chapter Goals

- To be able to read and write files
- To convert between strings and numbers using string streams
- To learn how to process the command line
- To understand the concepts of sequential and random access
- To be able to build simple random-access database files
- To learn about encryption

## Reading and Writing Text Files

- To access a disk file, you need to open a file variable.
  - type `ifstream` for input

    >     ifstream input_data;

  - type `ofstream` for output
  - type `fstream` for both input and output
- To read anything from a file, you need to open it.

  >     input_data.open("input.dat");

- To read from the file, use the name of the `ifstream` variable in place of `cin`.

  >     int n;
  >     double x;
  >     input_data >> n >> x;

- You read in strings in the usual way.

  >     string s;
  >     input_data >> s; /* read a word */
  >     getline(input_data, s); /* read a line */

## Reading and Writing Text Files

- You read in a single character with a get method.

  >     char ch;
  >     input_data.get(ch);

- If you read a character and you regretted it, you can unget it, so that the next input operation can be read again.

  >     char ch;
  >     input_data.get(ch);
  >     if ('0' <= ch && ch <= '9') /* it was a digit */
  >     {
  >        input_data.unget(); /* oops - didn't want to read it */
  >        int n;
  >        input_data >> n; /* read integer starting with ch */
  >     }

- This is called one-character lookahead: at the next character in the input stream you can make a decision what you want to read in next, but not more than one character.
- Older implementations of the stream library do not have the `unget` member function - use`input_data.put_back(ch)` instead.
- When you are done using a file, you should close it.

  >     input_data.close();

## Reading and Writing Text Files

- Writing to a file is similar.
- Open a file for writing.

  >     ofstream output_data;
  >     output_data.open("output.dat");

- Send information to the file in the usual way.

  >     output_data << n << " " << x << "\n";

- To write a single character to a file use

  >     output_data.put(ch);

- When you are done, remember to close the file.

  >     output_data.close();

## Reading and Writing Text Files

- Older implementations of the stream library may require a second parameter to open a file.

  >     datafile.open("employee.dat", ios::in | ios::out).

- The file name that you give the `open` command may be a string constant

  >     input_data.open("input.dat");

  or a `string` variable that contains a file name.

  >     string input_name =
  >        cwin.get_string("Please enter the file name:");
  >     ifstream input_data;
  >     input_data.open(input_name);

- Many compilers don't support `string` parameters for the `open` function, so you must use the `c_str` function to convert the parameter to a character array.

  >     input_data.open(input_name.c_str());

- File names can contain directory path information, but you must supply the escape character for a backslash.

  >     input_data.open("c:\\homework\\input.dat");

- Always pass streams by reference!!

## Reading and Writing Text Files (`maxval1.cpp`)

## The Inheritance Hierarchy of Stream Classes

- The C++ input/output library consists of several classes that are related by inheritance.

## The Inheritance Hierarchy of Stream Classes

- The standard `cin` and `cout` objects belong to specialized system-dependent classes with nonstandard names.
- You can assume that `cin` belongs to a class that is derived from `istream` and `cout` belongs to a class derived from `ostream`.
- Take advantage of the inheritance relationships between the stream classes whenever you write functions with stream parameters.

  >     double rand_data(istream& in);

- You can now pass parameters of types derived from `istream`, such as a `ifstream` object or `cin`.

  >     max = read_data(infile);
  >     max = read_data(cin);

- Remember to pass the streams by reference to avoid slicing.

## The Inheritance Hierarchy of Stream Classes (`maxval2.cpp`)

## String Streams

- The `istringstream` class (`#include<sstream>`) reads characters from a string, and the `ostringstream` class writes characters to a string.
- Using an `istringstream`, you can read numbers that are stored in a `string` by using the `>>` operator.

  >     string input = "January 23, 1955";
  >     istringstream instr(input);
  >     string month;
  >     int day;
  >     string commma.
  >     int year;
  >     instr >> month >> day >> comma >> year;

- Note that this input statement yields a day and year as integers. Had we taken the string apart with `substr`, we would have obtained only strings.
- Converting strings that contain digits to their integer values is such a common operation that it is useful to write a helper function for that purpose.

  >     int string_to_int(string s)
  >     {
  >        istringstream instr(s);
  >        int n;
  >        instr >> n;
  >        return n;
  >     }

## String Streams

- By writing to a string stream, you can convert numbers to strings.

  >     ostringstream outstr;

- By using the << operator, the number is converted into a sequence of characters.

  >     outstr << setprecision(5) << sqrt(2);

- To obtain a string from the stream, call the `str` member function.

  >     string output = outstr.str();

- Example: (builds the string "January 23, 1955")

  >     string month = "January";
  >     int day = 23;
  >     int year = 1955;
  >     ostringstream outstr;
  >     outstr << month << " " << day << "," << year;
  >     string output = outstr.str();

- Converting an integer into a string is such a common operation that is useful to have a helper function for it.

  >     string int_to_string(int n)
  >     {
  >        ostringstream outstr;
  >        outstr << n;
  >        return outstr.str();
  >     }

## String Streams

- A very common use of string streams is to accept input one line at a time and then to analyze it further.
- A typical example is when the program accepts input in a variety of formats.

  >     21:30
  >     9:30 pm
  >     9 am

- See `readtime.cpp`.

## String Streams (`readtime.cpp`)

## Command Line Arguments

- Starting a program by typing the name of the program at a prompt in a terminal or shell window is called "invoking the program from the command line."
- When you use this method, you can also type in additional information, called command line arguments, that the program can use.

  >     prog -v input.dat

- Only text mode programs receive command line arguments; the graphics library that comes with this book does not collect them.
- To receive command line arguments, you need to define the main function in a different way.

  >     int main(int argc, char* argv[])
  >     {
  >        . . .
  >     }

  - `argc` is the count of arguments.
  - `argv` contains the values of the arguments.
- Note that `argc` is an array of pointers to C character arrays.
- In the invocation above, `argc` is 3 and `argv` contains (pointers to) the strings

  > `argv[0]:` | `` | `"prog"`
  > `argv[1]:` | `` | `"-v"`
  > `argv[2]:` | `` | `"input.dat"`

- Note that `argv[0]` always points to the name of the program and that `argc` is always at least 1.

## Command Line Arguments

- The sample program (`caesar.cpp`) is used to encrypt a file - that is, scrambles it so that it is unreadable except to those who know the decryption method and the secret keyword.
- This program uses a method familiar to Julius Caesar.
  - The person performing any encryption chooses an encryption key (a number between 1 and 25).
  - The key indicates the shift to be used in encrypting each letter. (For example, if the key is 3, we replace A with D, B with E, etc.).

    > Plain Text | `` | `M` | `e` | `e` | `t` | `` | `m` | `e` | `` | `a` | `t` | `. . .`
    > | `` | `` | `` | `` | `` | `` | `` | `` | `` | `` | `` | ``
    > Encrypted Text | `` | `P` | `h` | `h` | `w` | `` | `p` | `h` | `` | `d` | `w` | `. . .`

## Command Line Arguments

- The program takes the following command line arguments:
  - An optional `-d` flag to indicate decryption instead of encryption.
  - An optional encryption key, specified with a `-k` flag. (If no key is specified, then 3 is used).
  - The input file name.
  - The output file name.
- Examples:

  >     caesar input.txt encrypt.txt
  >     caesar -d -k11 encrypt.txt output.txt

## Command Line Arguments (`caesar.cpp`)

## Random Access

- In the above, we read from a file an item at a time and wrote to a file an item at a time. That access pattern is called sequential access.
- Random access allows us to access specific location in a file and only change those locations.
- The term "random" means you can read and modify any character stored at any location in the file.

## Random Access

- Each disk file has two special positions:
  - The get position indicates the next read command starts reading input at the location.
  - The put position indicates where to write the next output.
- Normally the get position starts at the beginning of the file and is moved towards the end.
- Normally the put position always points to the end.
- By moving the get and put positions, you can achieve random access.

## Random Access

- The follow calls move the get and put positions to character `n` counted from the beginning of the file `fs`.

  >     fs.seekg(n, ios::beg);
  >     fs.seekp(n, ios::beg);

- The second argument can be `ios:end` or `ios::cur` to move the position `n` characters away from the end of the file or the current position, respectively.
- To determine the current position of the get and put positions (counted from the beginning of the file), use

  >     n = fs.tellg();
  >     n = fs.tellp();

- Because the files can be very larger, the positions are long integers.
- To find out the number of characters in a file, move the get position to the end and then find out the distance from the beginning of the file.

  >     fs.seekg(0, ios::end);
  >     long file_length = fs.tellg();

## Random Access

- When manipulating data with random access, we have to pay special attention to the formatting of the data.
- Example: Suppose we store the data as text:

  Then raise Harry's salary by 5.5 percent to $36,397.50.

  The update overwrites some characters in the next field.
- In order to be able to update a file, we must give each filed a fixed size that is sufficiently large.
- As a result, every record in the file has the same size, making it easy to jump over records.
- Example: We will restructure our records above to

  > Name: 30 characters
  > Salary: 10 characters

## Random Access (`database.cpp`)