Writing a program to read an input file

You have to write a program to read an input file character by character to help Peter solve the following activity in his activity book. The paragraph below is given:
We h2pe that 32u e5723ed the acti4it3. A6ter 32u ha4e c2mpleted the acti4it3, 0e5d 32ur re0ult t2: The Acti4it3 C2mpetiti25, Bett3 Da4i0 0treet 99, Auckla5d Park, 989, a5d 0ta5d a cha5ce t2 wi5 a hamper c250i0ti51 26 c2l2uri51 a5d acti4it3 b22k0, c2l2uri51 pe5cil0 a5d pe50.
Create an input file activity.dat with the paragraph above. The numbers 0 to 7 have to be replaced as follows:
0 must be replaced by s
1 must be replaced by g
2 must be replaced by o
3 must be replaced by y
4 must be replaced by v
5 must be replaced by n
6 must be replaced by f
7 must be replaced by j
Ask the user to input the names of the input and output files. Read the input file character by character, and write the character (if it stays the same) to the output file, or write the changed character to the output file. Call your output file competition.txt


So,if im intepreting this right , I have to make a txt.file and copy paste the paragraph , then write a program to read that txt.file and then output it to another txt.file with the replacements?

If so, can someone just help with how you actually can do that? DON"T WRITE THE WHOLE CODE. just explain to me how you actually are able to let it read it and convert it?

Currently using code_blocks and doing it all via console
> then write a program to read that txt.file and then output it to another txt.file with the replacements?
Yes.

As a useful first exercise, just see if you can make an accurate copy of the input file without any substitutions.
1
2
3
4
5
6
7
8
9
10
11
char transform_char(char c) {
  return c;
}
int main ( ) {
  char c;
  // fin and fout are opened fstreams
  while ( fin.get(c) ) {
    c = transform_char(c);
    fout.put(c);
  }
}


The next step is make transform_char deal with just one of the substitutions.
You re-test and make sure your output file is heading in the right direction.
So,if im intepreting this right , I have to make a txt.file and copy paste the paragraph , then write a program to read that txt.file and then output it to another txt.file with the replacements?

This is more or less right and shows your plan/framework (pseudocode even), perhaps change a few things and expand on it as follows:

1. Make a .txt file ( call it some_text.txt )
2. Manually copy paste the paragraph into the file
3. Create a new file for output (output.txt)
4. Write a program to read that .txt file character by character
5a. --- Process each character by making replacements
5b.     Save processed character to output.txt file
5c.  --- Loop until there are no more characters
6. All done then close files.
make the text file in notepad to start: the assignment says to read the file, but not where it came from, so it is user provided not code generated.

now write code to open the file, read it in letter by letter (their choice, they told you do do it this way) and perform the lookup and exchange.

by the way, check this out:
unsigned char tbl[256];
std::iota(tbl, tbl+ 256, 0); //this is just filling the table with 0-255 which you can do in a loop if you want.
tbl['0'] = 's'; //0 and '0' are not the same. it must be in quotes here
tbl['1'] = 'g';
... fill in the rest of these...
the output you want is (ofs is ofstream ifs is ifstream variable name examples)
ofs << tbl[inputletter];

so basically with the above table all you need is:
ifs >> letter;
ofs << tbl[letter]; //when letter == '0' you get s, because of the table conversion.

there are examples in the reference section on this site on how to do the 2 files. they work a lot like cin and cout:
ifstream ifs(infilename); //opens infilename, but you need to check to see if it worked (that is, handle a bad file name from user)
ifs >> letter; //works like cin
ofstream ofs(outfilename); //you create or over-write this one, no need to check however it can fail if user said to make it in a read only place or nonexistent drive letter etc..! You can probably skip that for your homework, but know that you do need to check it in a serious program
ofs << letter; //works like cout

you can also check each letter with if statements or a switch statement to manually convert it.
if(letter == '0') .. do something
else if(letter == '1')
else if ... //over and over
else default action ...
Last edited on
You can also use 2 strings - before and after. If find read char in before string then replace with after char std::string.find()

1
2
const std::string before {"01234567"};
const std::string after {"sgoyvnfj"};


ifs >> letter;

ignores white space unless you also use std::noskipws
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;
int main()
{
    string filename("activity.txt");
    vector<char> bytes;
    char byte = 0;

    ifstream input_file("activity.txt");
    if (!input_file.is_open()) {
        cerr << "Could not open the file - '"
             << "activity.txt" << "'" << endl;
        return EXIT_FAILURE;
    }

    while (input_file.get(byte)) {
        bytes.push_back(byte);
    }
    for (const auto &i : bytes) {
        cout << i << "-";
    }
    cout << endl;
    input_file.close();

    return EXIT_SUCCESS;
}


Here is my reading code so far, it worked , it displayed the words on the console.

I am now trying to figure out how to let that string work you gave me @seeplus
Last edited on
@OP
It's possible to use <vector>'s and <string>'s but given you starting post it's not necessary. You're simply reading a character, which is all you will do with <string> functionality and the <vector> would only be useful if you wanted to store the data in memory for some (unknown?) reason.
The challenge is to come up with a way of efficiently performing the replacements and, aside from other possibilities, a couple of possibilities have already been mentioned - switches and if's - there are other ways too with mapping the characters.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
#include <fstream>

using namespace std;

int main()
{
    string filename("activity.txt");
    char character{};

    // SETUP READ FILE
    ifstream input_file("activity.txt");
    if (!input_file.is_open())
    {
        cerr << "Could not open the file - '"
        << "activity.txt" << "'" << endl;
        return EXIT_FAILURE;
    }

    // SETUP WRITE FILE
    ofstream output_file("activity_output.txt");
    if (!output_file.is_open())
    {
        cerr << "Could not open the file - '"
        << "activity_output.txt" << "'" << endl;
        return EXIT_FAILURE;
    }

    while (input_file >> character) // NEVER EVER FORGET TO USE noskipws
    {
        // DISPLAY WHAT IS BEING READ IN
        cout << character << '\n'; 

        // PROCESS THE CHARACTER REPLACEMENTS (THIS IS JUST A DEMO/TEST
        if(character == 'e')
            character = '*';

        // WRITE RESULTS TO OUTPUT FILE
        output_file << character;

    }

    // TIDY UP
    input_file.close();
    output_file.close();

    return EXIT_SUCCESS;
}
Last edited on
to figure out how to let that string work you gave me @seeplus


Perhaps:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <fstream>
#include <string>

int main()
{
	const std::string before {"01234567"};
	const std::string after {"sgoyvnfj"};

	std::ifstream input_file("activity.txt");
	std::ofstream output_file("competition.txt");

	if (!input_file || !output_file)
		return (std::cerr << "Could not open files\n"), 1;

	for (char ch {}; input_file.get(ch); output_file.put(ch))
		if (const auto fnd {before.find(ch)}; fnd != std::string::npos && fnd < after.size())
			ch = after[fnd];
}

Last edited on
1
2
const std::string before {"01234567"};
	const std::string after {"sgoyvnfj"};

@OP
I misread the significance of <string>'s in the earlier post thinking it meant reading the file into a string. One of the few occasions in my life where I have to take back a comment for being wrong. Nevertheless that's another way of replacing the chars., and is one example of a mapping instead of a pile of if's or a switch which don't lend themselves to re-usability.
As you can see none of these require <vector>'s
 
ifs >> character;


As per my previous comment, this ignores white space chars unless you also use std::noskipws
that is why I wasted the space for a full ascii table. If you go my route, it is simply

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 while (input_file >> character)
    {
        // DISPLAY WHAT IS BEING READ IN
        cout << character << '\n';
      
        output_file << tbl[character];
       /*
         what happens is that every letter except the replaced ones is mapped to itself, so e is 
replaced with e,  x is replaced with x, and so on... but the special ones are replaced as specified. 
Its faster (no find, no conditions) and easier to write at the cost of memory AND my technique is
 not really valid for unicode problems, unicode is too big to map this way. 
      */

    }
Last edited on
I agree this is faster :)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <fstream>
#include <string>
#include <numeric>

int main()
{
	const std::string before {"01234567"};
	const std::string after {"sgoyvnfj"};
	unsigned char tbl[256] {};

	std::iota(tbl, tbl + 256, static_cast<unsigned char>(0));

	for (size_t i = 0; i < before.size() && i < after.size(); ++i)
		tbl[before[i]] = after[i];

	std::ifstream input_file("activity.txt");
	std::ofstream output_file("competition.txt");

	if (!input_file || !output_file)
		return (std::cerr << "Could not open files\n"), 1;

	for (char ch {}; input_file.get(ch); output_file.put(tbl[ch]));
}


I don't like many assignments! :)

@OP
Lot's of options and lots of choices to make now.
0p2ilt 62r ch2ice
Another way to translate the characters is a switch statement. This might be clearer to a beginner, but jonin's lookup table is probably faster.
1
2
3
4
5
6
7
8
while (infile.get(ch)) {
    switch(ch) {
        case '0': ch = 's'; break;
        case '1': ch = 'g'; break;
        // etc.
    }
    outfile.put(ch);
}

No surprises with a <map> option:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
...
#include <map>

using namespace std;

int main()
{

    map<char,char> character_map;
    map<char,char>::iterator iter;

    character_map['0'] = 's';
    character_map['1'] = 'g';
    character_map['2'] = 'o';
    character_map['3'] = 'y';
    character_map['4'] = 'v';
    character_map['5'] = 'n';
    character_map['6'] = 'f';
    character_map['7'] = 'j';

   ...


// PROCESS THE CHARACTER REPLACEMENT
        iter = character_map.find(character);
        if( iter != character_map.end() )
            character = iter -> second;
...

    return EXIT_SUCCESS;
}
ty all very much , I actually learned quite a lot today
my understanding is that most of the time, unless prevented by something, switches ARE lookup tables when compile. Its probably about the same, and in turn, the map is also pretty close to what I did.
Last edited on
Topic archived. No new replies allowed.