Simple substitution?

My assignment was to getline from a file then match to an array of chars to replace with another array of chars. I already did the same with spaces.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    char plain[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
            'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
            'W', 'X', 'Y', 'Z' };
    char cipher[] = { 'E', 'M', 'N', 'V', 'D', 'L', 'W', 'A', 'U',
            'C', 'P', 'O', 'K', 'X', 'Q', 'B', 'I', 'Z', 'J', 'R', 'Y', 'T',
            'G', 'S', 'H', 'F' };
    char space_cipher[] = {'^', '~', '#', '`', ':'};

            while (getline(fileIn, line)) {
    
                int space_i = 0;
    
                for (int i = 0; i <= line.length(); i++) {
                    line[i] = toupper(line[i]);
                    if (line[i] == ' ') {
                        line[i] = space_cipher[space_i];
                        space_i = (space_i + 1) % 5;
                    }
                    fileOut.put(line[i]);
                    fileOut << endl;
                }
                cout << line << endl;
            }


But I couldn't figure out how to apply the hints given below.

1
2
3
4
5
    int j = 0;
    while (line[i] != plain[j]) {
        j++;
    }
    line[i] = cipher[j];


Can anyone walk me through?

Hello jvlinh,

It looks to me like the while is doing much the same as your for loop. I would try adding lines 19 and 20 after the for loop and try putting the whole bit of code after the for loop and before the close of the while loop at line 23.

for loops and while loops are much the same except the way they are used.

Hope that helps,

Andy
This is the same hint in no less cryptic syntax:
1
2
3
4
5
auto it = std::find( std::begin(plain), std::end(plain), line[i] );

if ( it != std::end(plain) ) {
  line[i] = cipher[ std::distance( std::begin(plain), it ) ];
}

Pay attention to highlighted word: find.

Find what from where?
From: array 'plain'
What: array element that has same character as the currently char in the 'line'

Your version uses index 'j' to point to the desired array element. The lines 1-4 perform the "find".
Try to think what the loop does, step by step, for example when line[i] is 'C'.
(The j should be 2 after the loop.)

Your line 5 then copies a char from 'cipher' using same index/offset that was just found. The C would be replaced with N.


Note: Your version goes berserk, if 'plain' does not contain the searched character.
(I could give "42" as input and none of ", 4, 2 is in plain.)
My version has a safety guard against that.
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <iostream>
#include <string>
#include <fstream>
#include <cassert>

int main() {

    // since we are using std::string, we might as well use it for these too
    const std::string plain =  "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ; // note: const
    const std::string cipher = "EMNVDLWAUCPOKKQBIZJRYTGSHF" ; // note: const
    // invariant: cipher.size() >= plain.size()
    assert( cipher.size() >= plain.size() ) ; // assert the invariant

    const std::string space_cipher = "^~#`:" ; // note: const

    std::string line ;
    std::ifstream fileIn( __FILE__ ) ; // test it with this file as input
    std::ofstream fileOut( __FILE__ ".modified.txt" ) ;

    while( std::getline( fileIn, line ) ) {

        // write the line to stdout so that we can see what it is
        std::cout << " plain text: " << line << '\n' ;

        std::size_t space_i = 0 ;

        for( std::size_t i = 0; i < line.length(); i++) { // *** note: < not <=
        // we could use a range-based loop here instead of the classical for loop 
        // for( char& ch : line ) { 
        // and then use ch instead of line[i] in the code below  
        // http://www.stroustrup.com/C++11FAQ.html#for
        // note that this is the preferred way in C++; range-based loops are not prone to the "off by one"
        // error ( <= in the original code) which is something we have to guard against in classical for loops         

            line[i] = toupper(line[i]);

            if (line[i] == ' ') { // if the character is a space

                // replace it with one of the characters in the space cypher
                line[i] = space_cipher[space_i];
                // next time around, use the next character in a circular manner
                space_i = (space_i+1) % space_cipher.size() ;
            }

            ///////////////////////////////////////////////////////////////
            const std::size_t pos = plain.find( line[i] ) ; // try to locate the character in plain
            if( pos != std::string::npos ) { // if it is a character found in plain

                    line[i] = cipher[pos] ; // replace it with the character at the same position in cipher
            }

            // the above code is equivalent to
            //
            // int j = 0;
            // while (line[i] != plain[j]) {
            //     j++;
            // }
            // line[i] = cipher[j];
            //
            // except that:
            // 1. instead of a loop, we use the find() function of std::string to locate the character
            // 2. we also cater to the possibility that line[i] may not be one of the characters in plain
            ////////////////////////////////////////////////////////////////
        }

        fileOut << line << '\n' ; // write out the modified line to the output file
                                  // do not forget to put a new line at the end of each line

        // and also to stdout so that we can see what was written
        std::cout << "cipher text: " << line << "\n\n" ;
    }
}
Last edited on
Topic archived. No new replies allowed.