On a C++ project, I have been trying to use an array to store data from a textfile that I would later use. I have been having problems initializing the array without a size. Here is a basic sample of what I have been doing:
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main()
{
int i = 0;
ifstream usern;
string data;
string otherdata;
string *users = nullptr;
usern.open("users.txt");
while(usern >> otherdata)
i++;
users = new (nothrow) string[i];
for (int n = 0; usern >> data; n++)
{
users[n] = data;
}
usern.close();
return 0;
}
This is a pretty rough example that I threw together. Basically I try to read the items from a text file called users.txt and store them in an array. I used pointers in the example that I included (which probably wasn't the best idea considering I don't know too much about poniters). When I run this program, regardless of the data in the file, I do not get any result when I try to test the values by including cout << *(users + 1). It just leaves a blank line in the window. I am guessing my error is in my use of pointers or in how I am assigning values in the pointers themselves. I was wondering if anybody could point me in the right direction on how to get the correct values into an array. Thanks!
Try reopening usern after
while(usern >> otherdata)
i++;
perhaps, try putting in
usern.close();
ifstream usern2;
usern2.open("users.txt");
right after that.
There may be other issues, but this seems like the most likely one to me. Let me know if you find success with this. To me it appears like usern is already reaching eof, and then you try to read from it a second time.
One thing that helps me a lot in finding such issues is to just put a cout << "looping"; or something inside the for loop so you know that you're at least getting in that for loop.
You can also do the same thing with usern.seekg(0, ios::beg);
What I think has happened in your code is that you have moved the pointer in the file that shows where the file is being read from. This happened when you iterated the number of strings to be read in using the code below.
while(usern >> otherdata)
i++;
This however brought the file pointer to the end of the file this means that in order to read the file you need to move the file pointer to the beginning of the file before you re-read it into your array of strings that you allocated of size i. This can be acheived by adding usern.seekg(0, ios::beg); after your while loop, as shown below. (For a good tutorial on file pointers see here.)
while(usern >> otherdata)
i++;
// Returns file pointer to beginning of file.
usern.seekg(0, ios::beg);
// The rest of your code.
Warning: I am unsure about how safe dynamically allocating STL containers are, I have previously run into issues with code similar to yours and would recommend staying away from this in functional code.
Related
I need to pull text line by line out of my .txt file and store it into a dynamic array that has new space allocated every time I pull a new line out of the .txt file. My code seems to pull out the first line just fine and store it into the first pointers array, but on the second loop, it seems to reset all the pointers arrays which gives me memory allocation errors when I later try to access it. Why does this happen especially when I don't touch the pointers and their arrays after I store stuff into them?
char** temp = nullptr;
char buffer[256];
int index = 0;
// Open File
fstream myFile;
myFile.open("pantry.txt", ios::in);
if (myFile.is_open())
{
while (!myFile.eof())
{
myFile >> buffer; // Pull line out of txt.file
temp = new char* [index + 1]; // Create new pointer
temp[index] = new char[strlen(buffer)+1]; // Create char array pointed at by new pointer
#pragma warning(suppress : 4996) // Turns off complier warning
strcpy(temp[index], buffer); //Copy buffer into new char array
index++; // Increment our index counter int
}
for (int i = 0; i < index; i++)
{
cout << temp[i] << endl;
}
If allocated and stored correctly I want it to just print out the txt file exactly.
Instead, I get
Exception thrown at 0x7B9A08CC (ucrtbased.dll) in PE 12.4.exe: 0xC0000005: Access violation reading location 0xCDCDCDCD.
pantry.txt
Basil
Flat Leaf Parsely
Thyme
Sage
Cumin
Steak Seasoning
Mace
Garlic Powder
There are multiple bugs in the shown code.
while (!myFile.eof())
This is always a bug that also must be fixed, in addition to the main problem with the shown code:
temp = new char* [index + 1];
To help you understand the problem with this line, it's helpful to remember The Golden Rule Of Computer Programming:
Your computer always does exactly what you tell it to do instead of
what you want it to do.
According to the Golden Rule, the above line tells your computer, exactly: "new something, and assign it to temp".
And this is what your computer will do every time it executes this line. This line is executed once, on each iteration of this loop. The next time this loop runs, the previously newed temp will be replaced by another one, leaking everything that it pointed to before. Why should your computer do anything else, on this line? After all, this is exactly what you told your computer to do. And this is why you observed that this will "reset all the pointers arrays" on every iteration of the loop, resulting in "memory allocation errors".
In any case, this entire chunk of logic needs to be scrapped and rewritten from scratch, this time using the right logic. The simplest thing to do is to actually use the C++ library's std::vector and std::string objects, which will do all the memory allocation for you, correctly. Modern C++ code rarely needs to new anything, and will use C++ library's containers, instead.
It is possible that the goal of your assignment is to demonstrate proper use of low-level memory allocation and deallocation logic. In this case you will need to find some other way of doing this. Since you don't know the number of lines in advance, one approach will be to build a linked list, one line at a time, as each line gets read from the file. The final array, with all the character pointers gets allocated only after the entire file is read (and the number of lines is known), the pointers moved to the array, and the temporary linked list deleted. Or, perhaps implement a std::vector-like algorithm that progressively allocates a new pointer array, when it is full, copies over all the character pointers to a bigger array and then deletes the original one.
Of course, that's a lot of work. But unless the purpose of your assignment or task is to correctly implement low-level memory allocations and deallocation, why go through all the work and pain to do what std::vector and std::string already do, when you can simply use them, in just five or six lines of code, that will replace all of the above?
I have previously used C++, but I can definitely say I am not a pro. Not really even at coding in general. I'm trying, as much as it may not look like it. :/
My task is to create an array of structres of the type, "book_list".
the struct looks like this:
struct book_list
{
int bookNumber; string bookTitle; string bookAuthor;
string bookDescription; int bookMonth; int bookYear; int bookRating;
};
It's easy enough to create the array itself:
book_list myBookList[50];
Here's where I'm having trouble:
The program is intended to search for a .txt file. If it finds it, it will read into the array of structures all the data contained within the file. So, bookNumber with the .txt file's bookNumber, etc.
I have been looking up potential solutions all day, and I've looked into ideas such as ifstream, getline, using iterators, etc. My research has been as scattered as my brain, and I think I am a little stumped.
Please help, and if you need any further info, please let me know!
Thanks,
-Jon
You need to either read/parse each entry separately or read the entire file in then parse it. Since you're storing all the structs in memory anyway, the latter is probably fine if you don't want to bother with reading one at a time, but for educational purposes, you might want to try reading one at a time. Maybe separate by lines?
Then you need a way to parse the entries. There's no one way to do this, but the easiest is probably to use a JSON-formatted text file, mainly because there are libraries that can use it and tools that can help visualize it. JSON lets you represent dictionaries, arrays, strings, numbers, and booleans. You can either do this manually without much effort since you don't have anything fancy like nested structures, or you can use a library like this to parse JSON for you: https://github.com/DaveGamble/cJSON
(which is in C, will have to link with C++ if you want to use C++)
Assuming that the question may be formulated as:
can you give me a tiny programming example that can kickstart my exercise:
program p:
#include <iostream>
using namespace std;
int main() {
int num;
string buf;
while (true) {
cin >> num; if (cin.eof()) break;
cin >> buf; if (cin.eof()) break;
cout << "num is: " << num << " buf is: " << buf << endl;
}
}
text file t.txt
1
one
2
two
you can run
p < t.txt
and see that the output is what you probably expected.
Now of course change cin >> num; into cin >> booklist[0].bookNumber; etc. and your exercise will be on the right track.
Use <stdio.h> for FILE.
char *name;
FILE *file;
name = "database.txt";
file = fopen(name,"w+");
book_list myBookList[50];
While writing use this.
book_list newRecord;
newRecord.bookNumber = 0;
//other assignments
fseek(file,0,SEEK_END);
fwrite(&newRecord,sizeof(book_list),1,file);
fclose(file);
While reading use below.
file = fopen(name,"r+");
book_list record;
fseek(file,0,SEEK_SET);
int i=0;
while(true){
fread(&record,sizeof(book_list),1,file);
if(feof(file))
break;
myBookList[i].bookNumber = record.bookNumber;
//other assignments
i++;
}
fclose(file);
Note: I'm not sure this is working properly with string class.I have used this with char[SIZE] and strcpy. My suggestion is try this first with built-in types like int, float, char etc.
I apologize if this doesn't make sense. I Am not sure what to google.
Lets say I have two arrays
string a_1[16];
string a_2[20];
I need to output these to a file with a function, first, a_1[0] to a_1[n].
Then reads in the a_2's.
It's also possible to run the function again to add in more a_1's and a_2's to the output file.
so the format will be:
//output_file.txt
a_1[0].....a_1[n]
a_2[0].....a_2[M]
a_1[n+1]...a_1[16]
a_2[M+1]...a_2[20]
my question is. Is there a way to read output_file.txt back so that it will read in all of the a_1's to be in order, a_1[0] to a_1[16].
and then input a_2[0] to a_2[20].
maybe just put "something" between each group so that when "something" is read, it knows to stop reading a_1's and switch to reading in for a_2....
What the OP calls "Something" is typically called a Sentinel or Canary value. To be used as a sentinel, you have to find a pattern that cannot exist in the data stream. This is hard because pretty much anything can be in a string. If you use, say, "XxXxXx" as your sentinel, then you have to be very careful that it is never written to the file.
The concept of Escape Characters (Look it up) can be used here, but a better approach could be to store a count of stored strings at the beginning of the file. Consider an output file that looks like
4
string a1_1
string a1_2
string a1_3
string a1_4
2
string a2_1
string a2_2
Read the cont, four, and then read count strings, then read for the next count and then read count more strings
OK, so you're thinking his sucks. I can't just insert a new string into a1 without also changing the number at the front of the file.
Well, good luck with inserting data into the middle of a file without totally smurfing up the file. It can be done, but only after moving everything after the insertion over by the size of the insertion, and that's not as trivial as it sounds. At the point in a programming career where this is the sort of task to which you are assigned, and you have to ask for help, you are pretty much doomed to reading the file into memory, inserting the new values, and writing the file back out again, so just go with it.
So what does this look like in code? First we ditch the arrays in favour of std::vector. Vectors are smart. They grow to fit. They know how much stuff is in them. They look after themselves so there is no unnecessary new and delete nonsense. You gotta be stupid not to use them.
Reading:
std::ifstream infile(file name);
std::vector<std::string> input;
int count;
if (infile >> count)
{
infile.ignore(); // discard end of line
std::string line;
while (input.size() < count && getline(infile, line))
{
input.push_back(line);
}
if (input.size() != count)
{
//handle bad file
}
}
else
{
// handle bad file
}
and writing
std::ofstream outfile(file name);
if(outfile << output.size())
{
for (std::string & out: output)
{
if (!outfile << out << '\n')
{
// handle write error
}
}
}
else
{
// handle write error
}
But this looks like homework, so OP's probably not allowed to use one. In that case, the logic is the same, but you have to
std::unique_ptr<std::string[]> inarray(new std::string[count]);
or
std::string * inarray = new std::string[count];
to allocate storage for the string you are reading in. The second one looks like less work than the first. Looks are deceiving. The first one looks after your memory for you. The second requires at least one delete[] in your code at the right pace to put the memory away. Miss it and you have a memory leak.
You also need to have a variable keeping track of the size of the array, because pointers don't know how big whatever they are pointing at is. This makes the write for loop less convenient.
I have been working on an assignment for class and I keep getting an error I cant see to fix. Heres a portion of my code.
#include <iostream>
#include <string>
#include <fstream>
#include <iomanip>
using namespace std;
const int SIZE = 444;
int main()
{
char person1a[SIZE], person1b[SIZE], person2a[SIZE], person2b[SIZE], person3a[SIZE], person3b[SIZE],
person4a[SIZE], person4b[SIZE];
inData >> person1a[SIZE], person1b[SIZE], person2a[SIZE], person2b[SIZE], person3a[SIZE], person3b[SIZE], person4a[SIZE], person4b[SIZE];
return 0;
}
There is more to the code but i've narrowed the problem down to these lines. No matter how i seem to arrange them or what operators i use i always get a stack error at the first array. I've tried the obvious thing, to me at least, such as
inData >> person1a;
inData >> person1b;
etc
inData >> person1a >> person2b >> etc
and so on but I just cant get past this error.
Am I missing something obvious here, and if i am can anyone help me out?
For the record i know there are several topics about this already but im only taking my first programming class in school now and all the other topics have so much extra packed into them I cant understand it.
Your array is declared as:
char person1a[SIZE];
So valid subscripts while accessing the array are 0 to SIZE-1
With the statement:
inData >> person1a[SIZE], person1b[SIZE], person2a[SIZE], person2b[SIZE], person3a[SIZE], person3b[SIZE], person4a[SIZE], person4b[SIZE];
You are writing one past the memory allocated for each of the arrays.
Since, You are using C++ you should use std::string rather than c-style arrays.
It saves you all such problems and provides all the functionality that an character array provides you.
you are breaching the array boundaries when you access person1a[SIZE] etc
Array indecies are 0-based, therefore the largest index is SIZE-1 not SIZE
inData >> person1a[SIZE]
Assuming that inData is some kind of std::istream or similar, that streams a single character into element SIZE of the array. Since that is the size of the array, and valid indexes are in the range [0,SIZE-1], it writes beyond the array causing stack corruption.
If you intend to read a string, then read into the array itself:
inData >> person1a
Of course, this will also cause corruption if the user enters too many characters, so use std::string to store strings, rather than messing around with character arrays.
Also, to stream into more than one thing, chain >>:
inData >> person1a >> person1b >> ...;
Your code uses the comma operator, meaning that you're only actually streaming into person1a, and the rest of the statement has no effect.
#include <iostream>
using namespace std;
typedef struct
{
char streetName[5];
} RECORD;
int main()
{
RECORD r;
cin >> r.streetName;
cout << r.streetName << endl;
}
When I run this program, if I enter in more than 5 characters, the output will show the whole string I entered. It does not truncate at 5 characters. Why is that?
How can I get this to work correctly?
You are overflowing the buffer. Put another char array after streetName and you will likely find that it gets the rest of the characters. Right now you are just corrupting some memory on your stack.
In order to limit the input to the size of the receiving array you need to use the length-limiting facilities provided by your input method. In your case you are using cin, which means that you can specify the limit by using its width method
cin.width(5);
cin >> r.streetName;
Because cin sees streetName as a char * and writes to memory and there is nothing to stop writing to *(streetName + 6) and further. This is a form of buffer overrun
The best code in this case is define streetName as a std::string
i.e.
typedef struct
{
std::string streetName;
} RECORD;
Because you're overruning the end of your buffer and in this particular case you're getting away with it. C and C++ make it very easy to "shoot yourself in the foot", but that doesn't mean that you should.
It's a buffer overrun.
C++ does not perform bounds checking on array accesses, and memory does not simply stop at the end of the array. You are writing data to memory that is not part of the array, the consequences of which are non-deterministic, and may sometimes even appear to work.
It is quite likely that if you placed that code into a function, the program would crash when you tried to return from the function, because one likely possibility is that you will have dumped on the function return address on the stack. You may also have corrupted data belonging to the calling function.
The way to do this correctly in c++ is to use a std::string.
#include<iostream>
#include<string>
....
std::string r;
getline(cin, r);
std::cout << r <<std::endl;
For truncated input(with suitably defined and inited values).
while(cin.peek() != EOF && i < len)
{
cin >> arr[i];
++i;
}
You will want to do something after this to flush the buffer and not leave the rest of the line sitting on the input stream if you plan on doing other things with it.