2

I'm making a shift cipher that reads in text from a file and decodes it. The decryption works fine howver i can't figure out how to find the length of the file without hardcoding it into the size of the char array. It also only reads in one line, anything with a newline in corrupts.

Any help would be greatly appreciated, i've left out the main block of code as that deals with the array after it has been read in and seemed a bit long and irrelevant.

string fileName;
cout << "Please enter the locations of your encrypted text (e.g ""encryptedText.txt""): ";
getline( cin, fileName );
char encryptedMessage[446]; //How do i read in the file length and declare the array size as a variable instead of [446]?
char decryptedMessage[446];

ifstream in(fileName);
if(in.get(encryptedMessage, 446))
{
  [my decrypting code]
}
else
{
cout << "Couldn't successfully read file.\n";
}
system("pause");
Amro
  • 121,265
  • 25
  • 232
  • 431
Marobri
  • 213
  • 2
  • 5
  • 13
  • See the fourth test case in this [previous answer](http://stackoverflow.com/questions/3303527/how-to-pre-allocate-memory-for-a-stdstring-object/3304059#3304059). Change its `s4` from a `string` to a `vector`, and it should be pretty much ready to go. – Jerry Coffin Jan 18 '12 at 17:25

4 Answers4

4

Well, a simple one-liner for reading a whole file into a dynamically sized array (don't use a statically sized array) of chars would be:

#include <vector>
#include <iterator>

std::vector<char> encryptedMessage(std::istreambuf_iterator<char>(in),
                                   std::istreambuf_iterator<char>());

Don't mess with dynamic allocation yourself, just let std::vector do its job. And due to its optimized growth behaviour you don't really need to bother with checking the file size. Optimize for speed when neccessary or at least not before your files get larger than a few hundred characters. And of course the istreambuf_iterator (instead of istream_iterator) doesn't handle whitespace any special, it just takes each character raw from the file one by one.

You may do the same with a std::string instead of a std::vector<char>, but I'm not sure about its growth behaviour (maybe it always reallocates the array with one more element). But then again, who cares for speed when the file contains 400 charcters?

Christian Rau
  • 43,206
  • 10
  • 106
  • 177
2

You can use seekg to get the size of an entire file:

#include <iostream>
#include <fstream>
using namespace std;

int main () {
  long begin_byte, end_byte;
  ifstream in("example.txt");
  begin_byte = in.tellg();
  in.seekg (0, ios::end);
  end_byte = in.tellg();
  int total_bytes = end_byte - begin_byte;
  in.seekg(0, ios::begin);
  char *message = new char[total_bytes + 1];
  int index = 0;
  while (in) {
    message[index++] = in.get();
  }
  in.close();
  cout << "message is: " << message << endl;
  delete [] message;
  return 0;
}

You can read more about seekg, tellg and files in c++ as a whole here.

However a better solution then using char * is using a std:string and calling push_back on it while in has not ended:

#include <iostream>
#include <fstream>
#include <string>

using namespace std;

int main () {
  ifstream in("example.txt");
  string message;
  while (in) {
    message.push_back(in.get());
  }
  in.close();
  cout << "message is: " << message << endl;
  return 0;
}
Ivaylo Strandjev
  • 64,309
  • 15
  • 111
  • 164
  • 1
    Man, reading file character by character is just a terrible idea. You can read it in one go. –  Jan 18 '12 at 16:31
  • Agreed. But again you *need* to use something like the get function as decrypted_text sounds like something that will have special chars. There are better functions to read a chunk of the file with a given size. I could not find such a function that takes a string, though. – Ivaylo Strandjev Jan 18 '12 at 16:36
  • Right. That's why old school is using "open/stat/malloc/read" and new school falling for `ifstream` and `vector` :) Just beware that every time you show some code - newbies copy/paste it, and then we have a new generation of programmers... –  Jan 18 '12 at 16:41
  • Still... your comment got me thinking. Is there a way to read the whole file using ifstream and string without using an helper char* buffer? I thought about the read method, but there is no way to pass a pointer to the string data as its argument... – Ivaylo Strandjev Jan 18 '12 at 16:55
  • open in binary mode, then 'in.read(&message[0], total_bytes);'. one line and faster. Can do the same thing with string in C++11. Ben's answer shows this. – Mooing Duck Jan 18 '12 at 16:55
  • 1
    @istrandjev: Well, there is not. You can do sorta one-liner, but not as efficient as just reading the damn file into memory... See http://stackoverflow.com/questions/2602013/read-whole-ascii-file-into-c-stdstring –  Jan 18 '12 at 17:01
  • @Mooing Duck - I knew how to do it with vector but tried it with string once and it did not work(not c++11, though). – Ivaylo Strandjev Jan 18 '12 at 17:13
  • @istrandjev: In C++03, strings were optimized for copy on write, and other optimizations. Nobody used them. So in C++11, the requirements for strings were changed, and now reading into `&message[0]` of a string works, where it didn't before. – Mooing Duck Jan 18 '12 at 17:48
  • @VladLazarenko Although i admit being a complete noob, the reason i use Stack Overflow is due to how detailed the answers are explaining the code that i adapt into my program. Thanks for all of the responses. – Marobri Jan 19 '12 at 00:16
1

You cannot have Variable Length Arrays(VLA) in C++.
Compilers do provide VLA's as extensions but using them would make your code non-portable.

Simplest and Best Solution is to use std::string instead of character arrays.

You might get answers all over which advice you to use to use dynamically allocated arrays but using std::string is the best choice, so ignore those.

EDIT:
Since somebody downvoted this. I would be very interested in knowing the reasons(provided they are technical) to do so.

Alok Save
  • 190,255
  • 43
  • 403
  • 518
  • 2
    @VladLazarenko: Really? Please enlighten us with the reference which states VLA's are allowed by the C++ Standard. – Alok Save Jan 18 '12 at 16:16
  • How simple is it to search for individual characters in a string? (Edit: thanks for the answer!) – Marobri Jan 18 '12 at 16:16
  • 2
    @Marobri: As simple as [that](http://www.cplusplus.com/reference/string/string/find/) – Alok Save Jan 18 '12 at 16:18
  • @Als: Really. You can use VLA in C++. You can also allocate memory yourself, be that a chunk of stack memory or some memory from brk (). I don't think Marobri even knew about C99 VLA, to be honest. –  Jan 18 '12 at 16:20
  • 1
    @VladLazarenko: Repeat: Please enlighten us with the reference which states VLA's are allowed by the C++ Standard. – Alok Save Jan 18 '12 at 16:23
  • @Als: You use what you can, and not what you see in the standard. I mean, really, threads made it into C++ standard like what, 15 years after POSIX? And so you were sitting on SO and telling people not to use them? –  Jan 18 '12 at 16:28
  • 1
    @VladLazarenko: :) Thanks for giving me a hearty laugh at the end of an hectic day. – Alok Save Jan 18 '12 at 16:34
  • 1
    alloca_ is more portable than VLAs, use that instead. – Mooing Duck Jan 18 '12 at 16:52
  • The `std::vector` can also be used and *may* be preferred for cryptography, since it has similar functionality to an array. – Thomas Matthews Jan 18 '12 at 16:54
  • 1
    @VladLazarenko: I am not entirely sure if you are being serious here, regarding your 10k reputation. – Sebastian Mach Jan 18 '12 at 17:06
  • @EDIT Maybe because it doesn't give any answer to solve his problems, only general advice, which doesn't really buy him anything (I haven't been the downvoter, though). – Christian Rau Jan 18 '12 at 17:41
  • @ChristianRau: Not solve his problem? How? OP already acknowledged it helps him.If you mean *writing code* for the OP constitutes an answer,No I don't advocate that I am not doing that.OP can try the suggested solution,if any problems,come back show what was tried and I will gladly help out. – Alok Save Jan 18 '12 at 17:46
  • @Als He doesn't speak about VLAs in any way, so the first paragraph is rubbish anyway. Ok, now let's use `std::string` over dynamically allocated arrays. Now how do I get the size of the file to resize my string appropriately? Or do I not have to know the file size, well, how then to read the file contents into my string? Ah, and how do I get over the problem of only reading one line? Your answer only advises him to use string instead of char arrays. But that far he could come himself. Well, what now? Of course he thanks you for the answer, the general advice is good one, just no answer. – Christian Rau Jan 18 '12 at 17:53
  • @Als Of course just posting bare code snippets is not a good idea, but not providing any solution to the problem is neither. What's the shortest way from New York to Chicago? Well, use a car instead of a bicycle! Hmh, thanks! – Christian Rau Jan 18 '12 at 17:58
  • @ChristianRau: It seems you did not read the OP's Q correctly, or What it asks inside the code.I don't believe in providing ready made code for any reason-whatsoever.If OP has to learn he better learn the hard way.Even asking the right Q's OP could learn a lot but thanks to the *here's the code* answers OP doesn't have to think no more.I find this attitude of *here's the code* rather disappointing and depriving OP of learning something worthwhile than just copy pasting teh codes.Go on posting your ready made code answers.For that from me No Thanks! – Alok Save Jan 18 '12 at 18:02
0

You need dynamically allocated memory, and the best way to manage that is with std::vector.

std::vector<char> encryptedMessage;

encryptedMessage.resize(size_of_file);
in.get(&encryptedMessage[0], encryptedMessage.size());
Ben Voigt
  • 260,885
  • 36
  • 380
  • 671
  • I'm going to go on a limb and guess the OP doesnt know how to get the size of the file, and probably knew about vector. – Mooing Duck Jan 18 '12 at 16:49