I wanted to save the time into the existing txt file so that I can know when this particular record is added..I have this code to auto detect the time
time_t Now1;
struct tm * timeinfo;
char time1[20];
time(&Now1);
timeinfo = localtime(&Now1);
strftime(time1, 20, "%d/%m/%Y %H:%M", timeinfo);
Passports.Record_Added_On = time1;
cout << "\n\nRecord Added On: " << Passports.Record_Added_On;
code of reading the txt file:
fs = new fstream(Passports_FILE_NAME, ios::in | ios::out | ios::binary);
if (!fs)
{
cout << "\n Can't open or create '" << Passports_FILE_NAME << "' file" << "\n";
system("pause");
break;
}
recs_num = -1;
while (fs->read((char *)&Passports, sizeof(Passports)))
{
recs_num++;
if (Passports.ID == id && !Passports.Deleted)
break;
}
if (fs->eof()) //if (the record is not in the file || it's there but it's Deleted)
{
cout << "\nThe specific passports record does not exists in the file.";
closeFile(fs);
cout << "\n\nExit\n";
return 0;
}
it worked fine even when I display..however when I closed the program and open it again..
it shows weird characters like this or sometimes crashes..can anyone help me on this and explain what is the reason behind it?
You cannot initialize non POD objects ( like std::string class ) from memory. This leads to crashes because class does not know it is initialized. There is no guarantee that all class members where properly initialized from this code. Suppose that there was some class member which was pointer to some memory, allocated for you data. You saved it to file, then run your application again to load it. Then the pointer gets the same numeric value, but there is no more memory which he was pointing at. The memory must be allocated by OS, after OS function call, which was not called in our case. So next time you try to use this badly initialized class object - it crashed because it tries to work with memory which does not belongs to your application.
To fix this crash you should parse the file contents properly and then fill your Passports record accordingly.
For example:
int int_value;
std::string string_value;
fs >> int_value >> string_value;
after that you can initialize your object:
Passports.ID = int_value;
Passports.Name = string_value;
etc. The same way you should use to save data to the file ( this is called serialization, i recommend you search more on the topic ).
Related
myclass::myclass(queue<queue<char> > construction_info){
//why does this line crash?
queue<char> first_line_of_construction_info = construction_info.front();
construction_info.pop();
}
I am reading from text files (not generated by me so I can't change the format), into a queue of queue of char. It means lines of characters. And I process that info to generate the class. However, after working in a few debug messages I realized that the first time I am getting a bad_alloc on execute (the program initialized all myclasses from text files at startup) is this line in the code.
I'm new to working with C++ and my google-fu hasn't really helped me with this problem. Does anyone have any suggestions as to where I can start solve this crash?
Simply uncommenting the class constructor is letting my program work without any crashes, obviously without generating actually useful objects of course.
Using g++ with c++11 on linux.
Edit:
Here is the full code cut from the main file:
int initialize_classrooms(){
path p = "files/classrooms/";
//files of lines of queues of chars
//vector of vector of queue of char
vector<queue<queue<char> > > classroom_files;
if(exists(p)){
for (directory_entry& x : directory_iterator(p)){
queue<queue<char> > cur_file;
ifstream file(x.path().filename().string());
queue<char> cur_line;
char ch;
while (file >> noskipws >> ch) {
if(!isspace(ch)){
cur_line.push(ch);
}else if(ch == '\n'){
cur_file.push(cur_line);
cur_line = queue<char>();
}
}
classroom_files.push_back(cur_file);
cur_file = queue<queue<char> >();
file.close();
}
}else{
cout << "Classroom files are missing!" << endl;
return 1;
}
cout << "Got all the way to classroom creation" << endl;
int i = 1;
for(auto cf : classroom_files){
cout << "Number of loops: " << i << endl;
i++;
shared_ptr<classroom> cr = shared_ptr<classroom>(new classroom(cf));
}
cout << "Got past the classroom creation" << endl;
return 0;
}
If the goal is only to read the contents (of the queue at the front), then creating a (constant) reference is preferred.
queue<char> const& first_line_of_construction_info = construction_info.front();
^^^^^^
After "read"ing, it can be poped just as in current code.
EDIT: (Thanks to #Remy Lebeau)
Since copies are wastefule, myclass constructor can take construction_info by reference instead of by value.
myclass::myclass(queue<queue<char> > const& construction_info) {
^^^^^^
Look in the rest of your code too, you probably do not want multiple copies of these queue-of-queues floating around.
Aside: Unless otherwise constrained, instead of using a queue<char> for storing a line of text, consider using std::string.
I'm triying to implement my own MergeSort, but I've got some problems, see if anyone can help me a little.
I have a big file with some info separeted with coma (Name,city,mail,telf). I would like to apply mergesort to order it, because I supose that the client computer wont have as much memory to do it in one try.
So, I split it into files of MAX_CUSTOMERS lines, and order them individually, all correct until here, but when I want to get the first two files and order them, I've got all the problems, I got repeated, ones and others dissapear, here's my code:
void MergeSort(string file1Name, string file2Name,string name){
printf("Enter MERGE SORT %s AND %s\n",file1Name.c_str(),file2Name.c_str());
string temp;
string fileName;
string lineFile1, lineFile2;
bool endFil1 = false, endFil2 = false;
int numCust1 = 0;
int numCust2 = 0;
int x1 = 0, x2 = 0;
ifstream file1;
file1.open(file1Name.c_str());
ifstream file2;
file2.open(file2Name.c_str());
ofstream mergeFile;
fileName = "customers_" +name +".txt";
cout << "Result file " << fileName << endl;
mergeFile.open("temp.txt");
getline(file1,lineFile1);
getline(file2,lineFile2);
while(!endFil1 && !endFil2){
if(CompareTelf(lineFile1,lineFile2)==1){
mergeFile << lineFile1 << endl;
if(!getline(file1,lineFile1)){
cout << lineFile1 << endl;
cout << "1st file end" << endl;
endFil1 = true;
}
}else{
mergeFile << lineFile2 << endl;
if(!getline(file2,lineFile2)){
cout << lineFile2 << endl;
cout << "2nd file end" << endl;
endFil2 = true;
}
}
}
if(endFil1){
//mergeFile << lineFile2 << endl;
while(getline(file2,lineFile2)){
mergeFile << lineFile2 << endl;
}
}else{
//mergeFile << lineFile1 << endl;
while(getline(file1,lineFile1)){
mergeFile << lineFile1 << endl;
}
}
file1.close();
file2.close();
mergeFile.close();
rename("temp.txt",fileName.c_str());
return;
}
Customer SplitLine(string line){
string splitLine;
string temp;
Customer cust;
int actProp = 0;
int number;
istringstream readLineStream(line); //convert String readLine to Stream readLine
while(getline(readLineStream,splitLine,',')){
if (actProp == 0)cust.name = splitLine;
else if (actProp == 1)cust.city = splitLine;
else if (actProp == 2)cust.mail = splitLine;
else if (actProp == 3)cust.telf = atoi(splitLine.c_str());
actProp++;
}
//printf("Customer read: %s, %s, %s, %i\n",cust.name.c_str(), cust.city.c_str(), cust.mail.c_str(), cust.telf);
return cust;
}
int CompareTelf(string str1, string str2){
Customer c1 = SplitLine(str1);
Customer c2 = SplitLine(str2);
if(c1.telf<c2.telf)return 1; //return 1 if 1st string its more important than second, otherwise, return -1
else return -1;
}
struct Customer{
string name;
string city;
string mail;
long telf;
};
If have some question about the code, just say it! I tried to use varNames as descriptive as possible!
Thanks a lot.
Your code seems quite good, but it has several flaws and one important omission.
One of the minor flaws is lack of initialization of Customer structure - you didn't provide a constructor to the struct, and do no explicit initialization of the cust variable. Hopefully string members are properly initialized by the string class constructor, but long telf may get any initial value.
Another one is lack of format checking in splitting an input line. Are you sure that every input line has same format? If there are lines with too many commas (say, comma inside a name) then the loop may incorrectly try to assign 'email' data to 'telf' member...
OTOH if there is too few commas, the 'telf' member may remain uninitialized, with a random initial value...
Together with the first one this flaw may lead to incorrect order of output data.
Similar problems arise when you use atoi function: it returns int but your variable is long. I suppose you have chosen long type because of the expected range of values - if so, converting input data to int may truncate significant part of data! I'm not sure what atoi does in that case, it may either return the result of converting some initial part of the input string or just return zero. Both values are wrong and lead to incorrect sorting, so you better use atol instead.
Next issue is reading first line from both input files. You don't check if getline() succeeded. If an input file is empty, the corresponding lineFile_num string will be empty, but endFil_num will not reflect that - it will still be false. So you again go into comparing invalid data.
Finally the main problem. Assume the file1 contents is 'greater than' (that is: goes after) the whole file2. Then the first line stored in lineFile1 results in CompareTelf() returning -1 all the time. the main loop copies the whole file2 into the output, and...? And the final while() loop starts with getline(file1,lineFile1) thus discarding the first line of file1!
Similar result happens with files consisting of records (A,C) and (B), to be merged as (A,B,C): first A and B are read in, then A is saved and C is read in, then B is saved and end of file 2 detected. Then while(getline(...)) cancels C in memory and finds end of file 1, which terminates the loop. Record C gets lost.
Generally, when the main merging loop while(!endFil1 && !endFil2) exhausts one of files, the first unsaved line of the other file gets discarded. To avoid this you need to store the result of the first read:
endFil1 = ! getline(file1,lineFile1);
endFil2 = ! getline(file2,lineFile2);
then, after the main loop, start copying the input file's tail with the unsaved line:
while(!endFil1) {
mergeFile << lineFile1 << endl;
endFil1 = !getline(file1,lineFile1);
}
while(!endFil2) {
mergeFile << lineFile2 << endl;
endFil2 = !getline(file2,lineFile2);
}
I try to realise an external merge sort (wiki) and I want to open 2048 ifstreams and read data to personal buffers.
ifstream *file;
file = (ifstream *)malloc(2048 * sizeof(ifstream));
for (short i = 0; i < 2048; i++) {
itoa(i, fileName + 5, 10);
file[i].open(fileName, ios::in | ios::binary); // Access violation Error
if (!file[i]) {
cout << i << ".Bad open file" << endl;
}
if (!file[i].read((char*)perfile[i], 128*4)) {
cout << i << ". Bad read source file" << endl;
}
}
But, it crashes with
Unhandled exception at 0x58f3a5fd (msvcp100d.dll) in sorting.exe: 0xC0000005: Access violation reading location 0xcdcdcdfd.
Is it possible to use so much opened ifstreams?
Or maybe it is very bad idea to have 2048 opened ifstreams and there is a better way to realize this algorithm?
Arrays of non-POD objects are allocated with new, not with malloc, otherwise the constructors aren't run.
Your code is getting uninitialized memory and "interpreting" it as ifstreams, which obviously results in a crash (because the constructor of the class hasn't been run not even the virtual table pointers are in place).
You can either allocate all your objects on the stack:
ifstream file[2048];
or allocate them on the heap if stack occupation is a concern;
ifstream *file=new ifstream[2048];
// ...
delete[] file; // frees the array
(although you should use a smart pointer here to avoid memory leaks in case of exceptions)
or, better, use a vector of ifstream (requires header <vector>):
vector<ifstream> file(2048);
which do not require explicit deallocation of its elements.
(in theory, you could use malloc and then use placement new, but I wouldn't recommend it at all)
... besides, opening 2048 files at the same time doesn't feel like a great idea...
This is C++.ifstream is non-POD, so you can't just malloc it: the instances need to get constructed
ifstream file[2048];
for (short i = 0; i < 2048; i++) {
itoa(i, fileName + 5, 10);
file[i].open(fileName, ios::in | ios::binary); // Access violation Error
if (!file[i]) {
cout << i << ".Bad open file" << endl;
}
if (!file[i].read((char*)perfile[i], 128*4)) {
cout << i << ". Bad read source file" << endl;
}
}
Besides that, opening 2048 files doesn't sound like a good plan, but you can figure that out later
The value 0xcdcdcdcd is used by VS in debug mode to represent uninitialized memory (also keep an eye out for 0xbaadf00d).
You are using malloc which is of C heritage and does not call constructors, it simply gives you a pointer to a chunk of data. An ifstream is not a POD (Plain Old Data) type; it needs you to call its constructor in order to initialize properly. This is C++; use new and delete.
Better yet, don't use either; just construct the thing on the stack and let it handle dynamic memory allocation as it was meant to be used.
Of course, this doesn't even touch on the horrible idea to open 2048 files, but you should probably learn that one the hard way...
You cannot open 2048 files, there is an operating system limit for open files
As far as I can see, you don't really need an array of 2048 separate ifstreams here at all. You only need one ifstream at any given time, so each iteration you close one file and open another. Destroying an ifstream closes the file automatically, so you can do something like this:
for (short i = 0; i < 2048; i++) {
itoa(i, fileName + 5, 10);
ifstream file(fileName, ios::in | ios::binary);
if (!file) {
cout << i << ".Bad open file" << endl;
}
if (!file.read((char*)perfile[i], 128*4)) {
cout << i << ". Bad read source file" << endl;
}
}
I have a program that stores a "friends" info into a struct array and writes it to a file. No problem there. But how would I be able to modify and/or delete a specific element in that struct array? Did some reading and it says I can't, unless I was to shift it all over by one after deleting in.
So I'm assuming I need to read it in, then remove it, and shift all other elements over by one and write it again...but how would I do this? Tried to include only the necessary code I have so far.
For modifying it, I would guess I'd read it in, then ask for the specific element # I want to change, then set all the values in that element to null, then allow the user to input new info? How would this code look?
struct FriendList
{
char screenname[32];
char country[32];
char city[32];
char interests[32];
short age;
};
int main()
{
FriendList friends[num_friends];
const int num_friends = 2;
// Gets user input and puts it into struct array and writes to file
case 3:
{ // Getting info and putting in struct elements
for (index = 0; index < num_friends; index++)
{
// Create Friend Records
cout << "Enter Screename " << endl;
cin.ignore();
cin.getline(friends[index].screenname, 32);
cout << "Country: " << endl;
cin >> friends[index].country;
cout << "City: " << endl;
cin >> friends[index].city;
cout << "Age: " << endl;
cin >> friends[index].age;
}
counting += index;
fstream infile;
infile.open("friends.dat", ios::out | ios::binary |ios::app);
if(infile.fail())
{ cout << "File not found!\n\t";
// exit
}
// Writing struct to file
infile.write((char*)&friends, sizeof(friends));
infile.close();
break;
}
// Delete a friend ???
case 5:
{ // Reading in file contents into struct friends
// Then????
fstream outfile;
outfile.open("friends.dat", ios::in | ios::binary);
outfile.read((char*)&friends, sizeof(friends));
break;
}
Yes, It can modify member of the struct. But you don't clear memory at the first, you will see garages in friends.dat.
in upper of main, you have better to add memset().
memset(&friends, 0, sizeof(friends));
And you use ios::app . I guess that friends is full-set datas. Then, you should remove ios::app ?
BTW, in late about C++, most of c++er don't use binary file for like this case. :)
Change is relatively easy - just read the right entry, update it in memory and write back. In order to delete I suggest the following:
Read all the entries after the one u need to delete
Write those entries in the offset of the deleted entries
Truncate the fuile to the new length
This is the trivial approach
It sounds like you want either a std::deque or a std::vector depending on usage.
If you are deleting items infrequently, then use the std::vector instead of a fixed array:
std::vector<FriendList> friends;
to add new friends:
friends.push_back(newFriend);
accessing a friend by index is the same as accessing an array:
friends[index]
to delete an entry in the vector, use erase() (not remove()!):
friends.erase(friends.begin() + index)
You could make a method delete, which pulls the friend after the one you want to delete, moves the info to the current struct, and continues until there are no more friends.
i'm writing my c++ project and in visual studio everything goes good but when i'm compiling it on ubuntu many things get wrong.
example:
int main (int argsNum, char* args[]){
Country* country = new Country("USA");
Military* military = new Military("Army",country);
Shalishut* shalishut = new Shalishut(military);
Manager* manager = Manager::GetInstance();
FileReader* fileReader = FileReader::GetInstance();
fileReader->ReadCityConfig(args,country);
fileReader->ReadRoadConfig(args,country);
fileReader->ReadMilitrayCampConfig(args,military);
military->ShowBases();
return 0;
}
void FileReader::ReadMilitrayCampConfig(char* args[], Military* military){
string line;
char inputFileName [MAX_FILE_NAME_LEN];
strcpy (inputFileName,args[3]);
ifstream myfile (inputFileName); //inputFileName
char* campName;
string cityName;
if (myfile.is_open()){
while (!myfile.eof()){ //until the end of file
getline (myfile,line); //separate each line.
if ((line.size() != 0) && (line[0] != '#')) {
campName = strtok(&line[0],",");
cityName = (string)strtok(NULL,",");
Shalishut::FixName(campName); Shalishut::FixName(&cityName[0]);
if (!(military->IsBaseExist(campName))){
if (military->GetCountry()->IsCityExist(cityName)){
Base* baseToAdd = new Base(campName,cityName);
if (baseToAdd != NULL){
military->AddBaseToMilitary(baseToAdd);
military->GetCountry()->FindCity(cityName)->AddBaseToCity(baseToAdd);
}
}
else cout << "ERROR: City named \"" << cityName << "\" does not exist, can't add base \"" << campName << "\" !" << endl<<endl;
}
else cout << "ERROR: Base Named \"" << campName << "\" is already exist in Military, can't create base!" << endl<<endl;
}
}
myfile.close();
}
else throw ExceptionMilitaryCampConfigFileFault(); /*cout << "ERROR: Unable to open MilitaryConfig file!"<< endl;*/
}
bool Country::IsCityExist(const string cityName){
map<string ,City*>::iterator itCities;
itCities = m_cities.find((string)cityName);
if (itCities != m_cities.end()) return true;
else return false;
}
void Shalishut::FixName(char* name){
int i;
name[0] = toupper(name[0]);
for (i=1 ; name[i] ; i++){
name[i] = tolower (name[i]);
}
}
}
The problem is that the program reads the cities and the roads, but when it reads the military camp i got:
" does not exist, can't add base "Hazerim" !
even though in the config file i have base in the same name.
remind: in visual studio it works perfectly!
Assuming the error message is actually ERROR: City named _____ does not exist, can't add base "Hazerim" I would look carefully at the capitalization/spelling of the cities and city-for-base in your inputs. They probably don't match.
Also using strtok on a std::string is just asking for trouble, as it's destructive and strings don't expect their internal state to be blown away randomly. There are method like find_first_of that will help you parse C++ strings.
Like others have said:
double check line endings (maybe run dos2unix on input files in lieu of a more robust / error=prone solution)
make sure the case of everything is correct, file names are case sensitive
be aware of where it is looking for files, make sure everything is in the CWD
I'd advise not messing around with std::string internals. I don't know that it's legal, and it certainly could cause problems. Use .c_str() to get the C-style string and copy it to a char [], or use string functions to parse the input.
To debug, put insome output statements so you can see what the string values are, or learn a bit about gdb and step through a short initialization run.
That cityname = (string)... is just plain ugly. Since you're not using cityname out of that scope, you can declare string cityname(...);, and cityname will always be initialized and will be defined close to where it's used.