Argh, I knew I would probably analyze your code before too long... ;-)
http://www.cplusplus.com/forum/beginner/12340/
You are trying to be too sneaky with the code -- it doesn't do what you think it ought to be doing. For example, on line 43 the comment is
//read first 20 characters of the message
-- but it actually only reads
one character. Then you go into the permutation loop while trying to read more characters.
Remember, a permutation cipher is a form of
block cipher -- meaning that it works on a complete group of values (characters) at a time.
So, if your cipher key is 4 3 2 1 0 (to reverse each group of five characters), then you must read the plaintext five characters at a time. For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
ifstream infile;
ofstream outfile;
const unsigned pkey [ 5 ] = { 4, 3, 2, 1, 0 };
char block[ 5 ];
while (infile)
{
// Read an entire block to be permuted
for (unsigned n = 0; n < 5; n++)
{
block[ n ] = '\0'; // EOF --> null characters in the block
infile.get( block[ n ] ); // Get the next character in the block (if not EOF)
}
// Permute it
permute( block, pkey );
// Write the permuted block
for (unsigned n = 0; n < 5; n++)
{
outfile.put( block[ n ] );
}
}
|
The permutation function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
void permute( char block[ 5 ], int pkey[ 5 ] )
{
char result[ 5 ];
// This puts the permuted data in result[]
for (unsigned n = 0; n < 5; n++)
{
result[ pkey[ n ] ] = block[ n ];
}
// Now, copy the result[] back to block[] in order to return the permuted data
for (unsigned n = 0; n < 5; n++)
block[ n ] = result[ n ];
}
|
Input Format
I am not sure I understand your input data. It appears that you want it to be a file something like:
; this line is ignored
; this line is also ignored
19
4 3 2 1 0
; this line is ignored as well
Caesar was no fool.
|
As it is, your code fails after reading the '
C' in "Caesar", because it tries to skip two lines before each character read...
I do not know your teacher's input requirements, but I don't think it a good idea to mix the permutation key with the codes to permute. I think it is a better idea to ask the user for the key, then operate over an entire file. For example (presuming you are using Unix):
% cat Scrambled.txt
aseaCsaw rf on 0.loo
% ./a.out
Please enter the name of the file to permute
> Scrambled.txt
Please enter the name of the file to write
> Unscrambled.txt
Please enter the permutation key.
The key must be between 3 and 20 integers long, in the range [0,N), with no repeats.
For example, "4 3 2 1 0" to reverse blocks of 5 characters.
> 4 3 2 1 0
% cat Unscrambled.txt
Caesar was no fool.
%
(Bold text represents what the user entered. The underlined zero shows where there would be a null character in the output -- based upon the code I gave above. In real life a null character would not likely print anything... but I'll mention more on the significance of this character below.)
Block Size
It also looks to me like you want to be able to handle different-sized blocks (given a permutation for each text to cipher/decipher). This is easy enough to do using the STL containers
string and
vector. The above code then becomes much easier:
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
|
vector <unsigned> get_and_validate_permutation_key();
string read_block( ifstream& infile, unsigned block_size );
string permute( const string& block, const vector <unsigned> & pkey );
int main()
{
ifstream infile;
ifstream outfile;
vector <unsigned> pkey;
string block;
// Get the input file name
string infilename;
cout << "Please enter the name of the file to permute\n>" << flush;
getline( cin, infilename );
infile.open( infilename.c_str() );
if (!infile)
{
cerr << "I could not open \"" << infilename << "\"\n";
return 1;
}
// Get the output file name
string outfilename;
...
// Get the permutation key
pkey = get_and_validate_permutation_key();
if (pkey.empty())
{
cerr << "Invalid permutation key.\n";
return 1;
}
// OK, now permute the entire input file (compare this with the code above)
while (infile)
{
block = read_block( infile, pkey.size() );
block = permute( block, pkey );
outfile << block;
}
// All done!
return 0;
}
|
The
read_block() routine must be sure to return a string of exactly
block_size characters long. If the source file doesn't have enough characters to fill a whole block, the extra characters must be set to something, like null or a space or something. (I really will talk about it below -- honest.)
The
permute() function works much like before, except that now you can just
return the resulting string instead of overwriting the argument data...
Oh, and
get_and_validate_permutation_key() must do what it says... read a bunch of integers from the user and make sure they all have the required characteristics. If invalid in any way, the returned value should be an empty vector so that the program will complain and quit. I suggest using
getline() to read a
string from the user, then use an
istringstream to get the integers out of the line (with a loop) and
push_back() each integer in the result vector. Don't forget to validate!
Padding
Now, for the significance of that null character -- this is only useful if you know that the null character is not part of the plaintext. That is typically the case for text files, so it is safe to do that.
However, if that is
not true, then you must know exactly how long the plaintext is, and truncate the last block before writing it out. Your code over on the other post seems to suggest that you know how long the plaintext is. Where and how you store that information is up to you. You might just store an extra byte (character) at the beginning of the cipher file that gives the size of the last block (== plaintext_size % block_size), and read/write it first when deciphering/ciphering the file. Etc.
Whew. That ought to help get you started. Good luck!