Coding a Book Cipher C++

Hi, I am trying to code for a "Book Cipher". I need to write code that encodes and decodes.

For encoding, I need to read a text file from the command-line and convert each character to a pair of numbers representing a line index(0-based) and a character index(0-based) for that line. Those two values together, when applied to the cipher file as a position, must lead to the same character being decoded.

For example there are two files to be read, (1)inputFile and (2)cipherFile:
inputFile contents:
my bad

cipherFile contents:
mary had
a little
lamb

I am trying to find the location of each character read from the inputFile in the cipherFile and report its location is stated above, lineIndex and characterIndex of that line. I have done this on paper but am having such a tough time coding it. I know, or at least I think I do, I need to open the files and read the inputFile character by character store them in a string arraay? and then find the occurences of those characters in the cipherFile. I am not sure how to do this and report its loacation.

For the example above the output could be:
2 2 'm' is found on line 2, character position 2 in the cipherFile
0 3 'y' is found on line 0, character position 3 in the cipherFile
1 1 ' ' a space found between 'a' and 'little' line 1, position 1
2 3 'b' is found on line 2, position 3 in the cipherFile
2 1 'a' is found on line 2, position 1 in the cipherFile
0 7 'd' is found on line 0, position 7 in the cipherFile

Any help would be greatly appreciated! I am still very new to programming and to the C++ language. Please, any guidance I can get would be greatly appreciated.

Thank you.
Last edited on
Hello d3sousa92. Please don't post the same request :)
https://cplusplus.com/forum/beginner/284610/
Last edited on
To get the locations of each character in a file:

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
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <map>

// read a text file line by line
std::vector<std::string> get_lines_in_file( const std::string& file_name )
{
    std::vector<std::string> lines_in_file ;

    if( std::ifstream file{file_name} )
    {
        std::string line ;
        while( std::getline( file, line ) ) lines_in_file.push_back(line) ;
    }

    return lines_in_file ;
}

// create a map: map characters to row,col positions
using row_and_col = std::pair<std::size_t,std::size_t> ;
using position_map = std::map< char, std::vector<row_and_col> > ;

position_map map_char_positions( const std::vector<std::string>& lines )
{
    position_map map ;

    for( std::size_t row = 0 ; row < lines.size() ; ++row )
        for( std::size_t col = 0 ; col < lines[row].size() ; ++col )
            map[ lines[row][col] ].emplace_back( row, col ) ;

    return map ;
}

int main()
{
    const std::string file_name = __FILE__ ; // this file

    for( const auto& [ch,positions] : map_char_positions( get_lines_in_file(file_name) ) )
    {
       if( std::isalnum( (unsigned char)ch ) )
       {
           std::cout << "'" << ch << "'  :  [ " ;
           for( auto[row,col] : positions ) std::cout << '(' << row << ',' << col << ") " ;
           std::cout << "]\n" ;
       }
    }
}

https://coliru.stacked-crooked.com/a/6a71960c4258363f
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
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int main()
{
   unsigned row[256]{}, col[256]{};

   ifstream incipher( "cipherFile" );
   string line;
   for ( int r = 0; getline( incipher, line ); r++ )
   {
      for ( int c = 0; c < line.size(); c++ )
      {
         unsigned char i = line[c];
         row[i] = r + 1;
         col[i] = c + 1;
      }
   }

   ifstream in( "inputFile" );
   while( getline( in, line ) )
   {
      for ( unsigned char ch : line )
      {
         if ( row[ch] )
         {
            unsigned r = row[ch] - 1, c = col[ch] - 1;
            cout << r << " " << c << " '" << ch << "' is found on line " << r << ", position " << c << " in the cipherFile\n";
         }
         else
         {
            cout << ch << " is not found in the cipherFile\n";
         }
      }
   }
}


2 2 'm' is found on line 2, position 2 in the cipherFile
0 3 'y' is found on line 0, position 3 in the cipherFile
1 1 ' ' is found on line 1, position 1 in the cipherFile
2 3 'b' is found on line 2, position 3 in the cipherFile
2 1 'a' is found on line 2, position 1 in the cipherFile
0 7 'd' is found on line 0, position 7 in the cipherFile

As a 'proof of concept', possibly something like:

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 <fstream>
#include <iostream>
#include <map>
#include <string>
#include <utility>

using Encry_t = uint8_t;
using Pair = std::pair<Encry_t, Encry_t>;

int main() {
	if (std::ifstream cip { "cipherFile.txt" }) {
		std::map<char, Pair> cipher;
		std::map<Pair, char> decrypt;
		Encry_t lno {}, pos {};

		for (char ch; cip.get(ch); ) {
			if (ch == '\n')
				++lno, pos = 0;

			if (cipher.emplace(ch, std::pair { lno, pos }).second)
				decrypt[std::pair { lno, pos++ }] = ch;
		}

		if (std::ifstream ifs { "inputFile.txt" }) {
			if (std::ofstream ofs { "encrypt.txt" }) {
				for (char ch; ifs.get(ch); )
					if (const auto it { cipher.find(ch) }; it != cipher.end()) {
						ofs.write((char*)&it->second.first, sizeof(Encry_t));
						ofs.write((char*)&it->second.second, sizeof(Encry_t));
					} else
						std::cout << "'" << ch << "' not found in cipher!\n";

				ofs.close();

				std::ifstream efs("encrypt.txt");

				for (Encry_t l {}, p {}; efs.read((char*)&l, sizeof(Encry_t)); ) {
					efs.read((char*)&p, sizeof(Encry_t));
					if (const auto it { decrypt.find(std::pair { l, p }) }; it != decrypt.end())
						std::cout << it->second;
				}
			} else
				std::cout << "Cannot open output file\n";
		} else
			std::cout << "Cannot open input file\n";
	} else
		std::cout << "Cannot open cipher file\n";
}

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
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;

//--------------------------------------

class Code
{
   unsigned row[256]{}, col[256]{};
   vector<string> master;

public:
   Code( const string &cipherFile );
   bool encode( unsigned char ch, unsigned int &r, unsigned int &c );
   bool decode( unsigned int r, unsigned int c, unsigned char &ch );
};

//--------------------------------------

Code::Code( const string &cipherFile )
{
   ifstream in( cipherFile );
   string line;
   for ( int r = 0; getline( in, line ); r++ )
   {
      master.push_back( line );
      for ( int c = 0; c < line.size(); c++ )
      {
         unsigned char i = line[c];
         row[i] = r + 1;
         col[i] = c + 1;
      }
   }
}

//--------------------------------------

bool Code::encode( unsigned char ch, unsigned int &r, unsigned int &c )
{
   if ( row[ch] )
   {
      r = row[ch] - 1;
      c = col[ch] - 1;
   }
   return row[ch];
}

//--------------------------------------

bool Code::decode( unsigned r, unsigned c, unsigned char &ch )
{
   if ( r < master.size() && c < master[r].size() )
   {
      ch = master[r][c];
      return true;
   }
   return false;
}

//=============================================================================


int main()
{
   Code code( "cipherFile" );
   vector<pair<int,int>> check;

   ifstream in( "inputFile" );
   for ( string line; getline( in, line ); )
   {
      for ( unsigned char ch : line )
      {
         unsigned int r, c;
         if ( code.encode( ch, r, c ) ) cout << r << " " << c << " '" << ch << "' is found on line " << r << ", position " << c << "\n";
         else                           cout << ch << " is not found in the cipher definition file\n";
         check.push_back( { r, c } );
      }
   }


   // Back-check
   for ( auto pr : check ) 
   {
      unsigned char ch;
      if ( code.decode( pr.first, pr.second, ch ) ) cout << ch;
   }
}


2 2 'm' is found on line 2, position 2
0 3 'y' is found on line 0, position 3
1 1 ' ' is found on line 1, position 1
2 3 'b' is found on line 2, position 3
2 1 'a' is found on line 2, position 1
0 7 'd' is found on line 0, position 7
my bad
Hi lastchance,

thank you for your response. This has really cleared this up for me. I am curious though if I were to write the encoded text to a new file and then decode that encoded file to return the string "my bad" how would I do that?

For example using the exact same files as I mentioned above:
1. Open and read the file containing:
mary had
a little
lamb

2. Open and reading the now encoded file:
2 2
0 3
1 1
2 3
2 1
0 7

3. How would I find the characters associated with the lineIndex and characterIndex from the encoded file in the first file opened and then report the message to the console? Which should be "my bad"
Last edited on
1. Open and read the file containing:


That is already done by the line
Code code( "cipherFile" );
which calls Class Code's constructor with a file named "cipherFile".


The encoding of your message from a file named "inputFile" is done by
1
2
3
4
5
6
7
8
9
   ifstream in( "inputFile" );
   for ( string line; getline( in, line ); )
   {
      for ( unsigned char ch : line )
      {
         unsigned int r, c;
         if ( code.encode( ch, r, c ) ) ...
      }
   }

which encodes successive characters ch into integers r, c. You can simply write those r, c (for "row" and "column") values to another file.



1
2
3
4
5
6
7
2. Open and reading the now encoded file:
2 2
0 3
1 1
2 3
2 1
0 7


You can use another ifstream to read successive r, c values in.



3. How would I find the characters associated with the lineIndex and characterIndex from the encoded file in the first file opened and then report the message to the console?
Not sure what you are asking. The code already does precisely that.

At the moment it checks the encoding/decoding by stuffing the pairs {r,c} in a vector and successively decoding those characters back (so recovering your original message "my bad").

Last edited on
Sorry, maybe this will better clarify.
What I'm trying to do is, instead of storing the pairs in a vector and then decoding, read in the encoded message from the two files and find the character corresponding to the 2 2, 0 3, 1 1, etc..

File1:
mary had
a little
lamb

File2:
my bad

Encoded message:
2 2
0 3
1 1
2 3
2 1
0 7

So, if my two input files are:

File1
mary had
a little
lamb

File2
2 2
0 3
1 1
2 3
2 1
0 7

I want to decode File2 using File1, find the character in File1
2 2 'm' is found on line 2, position 2
0 3 'y' is found on line 0, position 3
1 1 ' ' is found on line 1, position 1
2 3 'b' is found on line 2, position 3
2 1 'a' is found on line 2, position 1
0 7 'd' is found on line 0, position 7

Therefore the decoded message is "my bad"

I guess the encoding in reverse. I hope this makes sense.

@d3sousa92,

The following lines take the pairs of integers (representing row and column) from the vector check. All you have to do is take those pairs from file rather than the vector.
1
2
3
4
5
   for ( auto pr : check ) 
   {
      unsigned char ch;
      if ( code.decode( pr.first, pr.second, ch ) ) cout << ch;
   }



We know what you want to do ... we just don't want to do ALL of it for you.


BTW, please don't create yet another thread on exactly the same topic.
Topic archived. No new replies allowed.