I am trying to read numbers from a file and store then in an array using dynamic memory. When I try to print a member of the array, it is showing the address instead of the actual contents.
// CLASS METHOD IMPLEMENTATIONS
#include "DataHousing.h"
// CONSTRUCTORS
DataHousing::DataHousing() {
}
void DataHousing::FillArray() {
int tempIn = 0;
int count = 0;
// attempt to open the file with read permission
ifstream inputHandle("NumFile500.txt", ios::in);
// count how many numbers are in each file
if (inputHandle.is_open() == true) {
while (!inputHandle.eof()) {
inputHandle >> tempIn;
count++;
}
// allocate memory for array
int* pFileContents = new int[count];
// fill array
while (!inputHandle.eof()) {
for (int i = 0; i < count; i++) {
inputHandle >> pFileContents[i];
}
}
cout << &pFileContents[2];
}
else {
cout << "error";
}
}
This is my first time attempting anything like this and I am pretty stuck. What am i doing wrong here?
The unary & operator is to retrieve an address, so it is quite natural that it is showing the address.
To display the contents, remove the & in cout << &pFileContents[2]; and have it display the contents.
Also the counting part of your code
while (!inputHandle.eof()) {
inputHandle >> tempIn;
count++;
}
has two mistakes.
Firstly, you are incrementing count without the last reading was successful.
Secondly, you are trying to read from ifstream that is already reached to EOF.
You have to clear the EOF flag and seek to the beginning of the file like this:
In conclusion, the counting part should be:
while (inputHandle >> tempIn) {
count++;
}
inputHandle.clear();
inputHandle.seekg(0, ios_base::beg);
I see that you're trying to print the required value using:
cout << &pFileContents[2];
Since pFileContents is an array, pFileContents[2] will access the second element (value) of the same.
But since you've prepended & before the element, it is going to print the address of the second element of the array.
In order to print the value of the second element of the array, just use:
cout << pFileContents[2];
Notice the difference in the later code, we haven't used & just after cout <<
Related
I was studying the STL and decided to write some code to practice the writing and reading of files. The problem consists of creating a list of int (0, 1,...,9), save it in a binary file, and finally load it again.
There are 5 basic code blocks:
1. create list
2. present list
3. save list
4. load list
5. present list again
It seems simple and straightforward; however, the code seems to get in an infinite loop.
int main(){
list<int> numbers;
/////// Create list of 10 integers ///////
for(int i=0; i<10; i++){
numbers.push_back(i);
}
/////// Present List ///////
cout << "List created: [";
list<int>::iterator it;
for(it = numbers.begin(); it != numbers.end(); it++){
if(*it != 9){
cout << *it << ", ";
}
else{
cout << *it;
}
}
cout << "]" << endl;
/////// Save list ///////
string fileName = "test.bin";
ofstream outputFile;
outputFile.open(fileName, ios::binary);
if(outputFile.is_open()){
outputFile.write(reinterpret_cast<char *>(&numbers), sizeof(numbers));
outputFile.close();
cout << "List saved to file." << endl;
}
else{
cout << "Could not open file named " << fileName << endl;
}
/////// Load list ///////
list<int> anotherList;
ifstream inputFile;
inputFile.open(fileName, ios::binary);
if(inputFile.is_open()){
inputFile.read(reinterpret_cast<char *>(&anotherList), sizeof(anotherList));
inputFile.close();
cout << "List loaded from file." << endl;
}
else{
cout << "Could not open file named " << fileName << endl;
}
/////// Present List ///////
cout << "List loaded: [";
for(it = anotherList.begin(); it != anotherList.end(); it++){
if(*it != 9){
cout << *it << ", ";
}
else{
cout << *it;
}
}
cout << "]" << endl;
return 0;
}
The problem is in the "Load List" code block, since, if I comment it out, everything works fine.
Am I saving the object correctly? What am I doing wrong?
Thanks in advance.
The problem lies in the flawed logic of reinterpret_cast<char *>(&numbers). Why?
std::list manages its storage using pointers. It simply holds a pointer to a chain of elements consisting of some objects and a pointer to the next element. You cannot simply treat it like a sequence of bytes and expect it to maintain its functionality.
What you instead need to do is to loop over the elements and write them to the file one by one:
#include <fstream>
#include <iostream>
#include <list>
int main() {
std::fstream file{};
file.open("data.txt", std::ios::binary | std::ios::out);
std::list<int> ints{2, 4, 5, 6, 8, 1, 3, 5, 7, 9};
for (int i : ints) {
file.write(reinterpret_cast<char*>(&i), sizeof(i));
}
file.flush();
file.close();
file.open("data.txt", std::ios::binary | std::ios::in);
ints.clear();
std::cout << "Before reading the file, size of the list is: " << ints.size() << '\n';
for (int i; file.read(reinterpret_cast<char*>(&i), sizeof(i)); ints.push_back(i));
for (int i : ints) {
std::cout << i << ' ';
}
}
Clarification of the second for loop:
for (int i; file.read(reinterpret_cast<char*>(&i), sizeof(i)); ints.push_back(i));
We declare a variable i since we need a place where we read the data. This one should be quite clear. We do not need to initialize i, since the condition of the loop will take care of that (although it would probably be a good practice to do it anyway).
The condition part: file.read(reinterpret_cast<char*>(&i), sizeof(i)). This may seem tricky at first, but it really isn't! First of all, we have a method call. We call std::basic_istream::read, specifying the two arguments - first, the memory address where to read the variable, and second, the number of bytes we want to read. The trick is that the read method not only reads and saves the data - it also returns the stream, so essentially after the data processing, we are left with the condition file. But it's not a bool, is it? Correct, it's not a bool (neither an int), but stream objects can be implicitly converted to bool, which is exactly what happens here! The rules are as follows: if the stream is in a correct state, the conversion returns true. It returns false otherwise. An incorrect state may be cauased, for example, by failure to read, which happens, for example, when you already have read the whole file. Essentially, this part both reads from the file and checks whether the reading process executed successfully. It's both the reading logic and the condition!
The third part: ints.push_back(i). Notice that this part only executes if the condition (reading from the file) executed successfully. It simply adds the read int (i) to the ints container.
All in all, you can read the for loop in the following way:
create a variable i, which will store, one by one, the variables from the file
as long as reading from the file is successful...
...add the read value to the container
outputFile.write(reinterpret_cast<char *>(&numbers), sizeof(numbers));
What you actually print is the binary representation of the list object itself. Unfortunately, it does not contain the data of the list directly, but instead looks similar to something like this:
template <typename T>
class list
{
struct node
{
node* next;
node* previous;
T data;
};
node* m_head;
node* m_tail;
size_t m_size;
public:
// ...
};
No direct link to the data. Even worse: With std::list, the data can get shattered all over your memory (in contrast to std::vector which assures contiguous data).
So you only can iterate over your list again (either with the iterator variant you chose already before or, more convenient, with a range based for loop):
for(auto n : numbers)
{
outputFile.write(reinterpret_cast<char*>(&n), sizeof(n));
}
Reading is different; you don't know the size in advance, do you? Well, there are ways to retrieve it (seekg, tellg), but that's more of interest if you want to read all the data into contiguous memory at once (you could reserve sufficient of in a std::vector), but that's another issue.
For the list approach:
int n;
while(inputFile.read(reinterpret_cast<char*>(&n), sizeof(n)))
{
anotherList.push_back(n);
}
this script is supposed to output array values that were inputted by the user into array "store." I am trying to store all the char array values into string temp. I get the error on line 12: "[Error] invalid conversion from 'char*' to 'char' [-fpermissive]." Would appreciate any help!
Edit: so I fixed the declaration and now at least it compiles, but the answer I get on my cmd is all jumbled up. Why is this so? The cmd only correctly couts the first string but after the space, it messes up.
#include <iostream>
#include <cstdlib>
using namespace std;
void coutArray(char[], int);
int main()
{
char store[50];
cout << "enter text: " << endl;
cin >> store;
coutArray(store, 50);
system("pause");
return 0;
}
void coutArray(char store[], int max)
{
string temp = "";
int i = 0;
while (i < max)
{
temp += store[i];
i++;
}
cout << temp << endl;
}
Using input from all answerers I finally got the fixed code:
#include <iostream>
#include <cstdlib>
#include <string>
using namespace std;
void coutArray(char[], int);
int main()
{
char store[50] = {0};
cout << "enter text: " << endl;
cin.getline(store, 50);
coutArray(store, 50);
system("pause");
return 0;
}
void coutArray(char store[], int max)
{
string temp = "";
int i = 0;
while (i < max && store[i]!=0)
{
temp += store[i];
i++;
}
cout << temp << endl;
}
Thanks everyone. i learned a lot!!!
When you get an input using "cin" your input automatically ends with 0 (NULL).
You just need to add one little piece of code to your while statement.
instead of this :
while (i < max)
use this :
while (i < max && store[i]!=0)
Now it will stop when the input string is finished and won't print any garbage existed in the array beforehand.
To show that cin does add terminating zero, i initialized the array to 46, and put a breakpoint after the cin
so I fixed the declaration and now at least it compiles, but the answer I get on my cmd is all jumbled up. Why is this so?
Not sure what you mean by jumbled up. But since you did not tell us what you typed its hard to know it looks like it worked to me:
> ./a.out
enter text:
Plop
Plop�ȏU�
Notice that since my input is only 4 characters long. This means that a lot of the characters in the array still have undefined (ie random values). This is why I am seeing junk. To get past this initialize the array to have all 0 values.
char store[50] = {0};
Even bettern use a C++ object than handles longer strings.
std::string store;
std::getline(std::cin, store);
Note: passing arrays to functions by value is not a good idea. On the other end they have decayed to pointers and thus do not act like arrays anymore (they act like pointers whose semantics are similar but not identical).
If you must pass an array pass it by reference. But I would use a C++ container and pass that by reference (it is much safer than using C constructs). Have a look at std::string
The declaration of the function is wrong. Should be void coutArray(char *, int);
Look at the Implicit Conversion rules to understand what the compiler can do and what it cannot to do for you.
The issue with your program was that you were probably entering in less characters than the maximum size of the buffer. Then when you passed the maximum size as the parameter to coutArray, you assigned unfilled slots in the char array to temp. These unfilled slots could contain anything, as you have not filled them up to that point.
Your program is still correct, but what would be better would be to use read so that the number of bytes you specify is the minimum number of bytes that can be entered:
std::cin.read(store, 50);
Even better solution would be to use std::string:
std::string store;
std::cin >> store;
// or for the entire line
std::getline(std::cin, store);
It also follows that your coutArray should be changed to:
void coutArray(std::string);
// ...
void coutArray(std::string str)
{
std::cout << str << std::endl;
}
Look at this way
template<typename T, size_t N>
void MyMethod(T (&myArray)[N])
{
//N is number of elements, myArray is the array
std::cout<<"array elements number = "<<N<<endl;
//put your code
string temp;
temp.resize(N+1);//this is for performance not to copy it each time you use += operator
int i = 0;
while (i < max)
{
temp += store[i];
i++;
}
cout << temp << endl;
}
//call it like this
char arr[] = "hello world";
MyMethod(arr);
I'm trying to create an array, write array to the file and than display it. It seems to be working but i get just part of the output (first 3 elements) or i get values over boundaries.
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
int arr[20];
int i;
for (i = 0; i < 5; i++)
{
cout << "Enter the value to the array: " << endl;
cin >> arr[i];
}
ofstream fl("numbers.txt");
if (!fl)
{
cout << "file could not be open for writing ! " <<endl;
}
for (i = 0; i < arr[i]; i++)
{
fl<<arr[i]<<endl;
}
fl.close();
ifstream file("numbers.txt");
if(!file)
{
cout << "Error reading from file ! " << endl;
}
while (!file.eof())
{
std::string inp;
getline(file,inp);
cout << inp << endl;
}
file.close();
return 0;
}
The terminating condition in the for loop is incorrect:
for(i=0;i<arr[i];i++)
If the user enters the following 5 ints:
1 0 4 5 6
the for loop will terminate at the second int, the 0, as 1 < 0 (which is what i<arr[i] would equate to) is false. The code has the potential to access beyond the bounds of the array, for input:
10 11 12 13 14
the for loop will iterate beyond the first 5 elements and start processing unitialised values in the array arr as it has not been initialised:
int arr[20];
which could result in out of bounds access on the array if the elements in arr happen to always be greater than i.
A simple fix:
for(i=0;i<5;i++)
Other points:
always check the result of I/O operations to ensure variables contain valid values:
if (!(cin >> arr[i]))
{
// Failed to read an int.
break;
}
the for loop must store the number of ints read into the arr, so the remainder of the code only processes values provided by the user. An alternative to using an array, with a fixed size, and a variable to indicate the number of populated elements is to use a std::vector<int> that would contain only valid ints (and can be queried for its size() or iterated using iterators).
while (!file.eof()) is not correct as the end of file flag will set only once a read attempted to read beyond the end of the file. Check the result of I/O operations immediately:
while (std::getline(file, inp))
{
}
its like hmjd said
for(i=0;i<arr[i];i++)
looks wrong
it should look like this
int size;
size=sizeof(your array);
for(i=0;i<size;i++)
Try this:
//for(i=0;i<arr[i];i++)
for(i=0;i<5;i++)
[EDITED]
I would initialize the array with 0 like this: int arr[20] = {0}; In this case you can use for example:
while ((arr[i] != 0 || i < sizeof(arr))
i<array[i]
It is wrong beacuse it comapres with the content of the array ,it does not check the size of array .
I have an array of Student objects. I set the array length to 100, but it doesn't have 100 valid Student objects in it. I want to be able to iterate through the array and grab all the valid Student objects, then stop when I get to an array cell that doesn't have a Student object.
I have tried putting NULL into the array cell after the last Student, and then checking if (queriedStudents[i]) as well as if(queriedStudents[i] != NULL), but neither has worked for me.
What is the best way to find the end of the used part of my array?
Student *Welcome::queryStudents(int *queries) {
int query = 0;
Student *matchedStudents[100];
int matchedPos = 0;
while (queries[query] > 0) {
for (int i = 0; i < numStudents; i++) {
if (allStudents[i]->id == queries[query]) {
matchedStudents[matchedPos] = allStudents[i];
matchedPos++;
}
}
query++;
}
matchedStudents[matchedPos] = NULL;
return *matchedStudents;
}
And my code chunk trying to print out each Student's values:
int i = 0;
while (i < 100) {
if (queriedStudents[i]) {
cout << "ID:\t" << queriedStudents[i]->id << endl;
cout << "Name:\t" << queriedStudents[i]->name << endl;
cout << "Addr.:\t" << queriedStudents[i]->address << endl;
cout << "Phone:\t" << queriedStudents[i]->phone << endl;
} else {
i = 100;
}
i++;
}
You've got a bigger problem. You declare the array matchedStudents on the stack in the function queryStudents. When control passes out of that function, the array passes out of scope. If you're trying to use it later (by means of the pointer it returns, which was the first element of the array) then you're messing with deallocated memory, which will almost certainly lead to undefined behavior. It's as if you're visiting a house that has changed owners since you were last there; there's no telling what's changed, and if you wander around with your eyes closed you might get into trouble.
You can declare the array on the heap:
Student **Welcome::queryStudents(int *queries) {
Student **matchedStudents = new *Student[100];
...
return matchedStudents;
}
Or pass it in by reference:
void Welcome::queryStudents(int *queries, Student **&matchedStudents) {
...
}
Either way, you can then tackle the problem of how to indicate the end of valid pointers. Your method looks feasible, but bear in mind that as #JerryCoffin has pointed out, std::vector is available. Arrays are a pain, and the STL containers (such as vector) were made to take care of these grubby details for you. These days working with arrays serves almost no purpose except pedagogy; play with them until you understand the concepts, then use more advanced containers which are base on them.
i am experiencing much trouble reading in files from input into an array struct. here is the code if someone can tell me what im doing wrong i can figure it out. the loop is supposed to be reading 2 strings, and 1 int, and skipping possible blank lines. but when i run it, it reads the first set and doesnt read nothing after that.
struct Instruments
{
string model;
string maker;
int year;
};
int main()
{
int size;
Instruments data[20];
int i =0;
ifstream fin;
fin.open("input.txt");
for (i=0; i<20; i++)
{
do{
getline(fin, data[size].model);
getline (fin, data[size].maker);
fin >> data[size].year;
size++;
}
while (data[size].model.length() > 0);
}
fin.close();
for(int i=0;i<size; i++)
{
cout << data[i].model << "model"<<endl;
cout << data[i].maker << "maker" << endl;
cout << data[i].year<< " year" << endl;
}
return 0;
}
There are multiple issues here:
Your first 'for' loop is using i as the loop counter but size as the array index.
After this call:
fin >> data[size].year
it will read to the end of the number and any whitespace that follows will form part of your next read, so if you are expecting to start the next record at the next line, do a blank getline() here too.
Aside from that.
Use vectors not arrays
Have a method to read from a stream into your struct, and if that succeeds, use push_back() to add it to your vector.
That doesn't necessarily mean you have to loop until the read fails, it may be that you know in advance how many you wish to read. But you should still do it this way.
size variable is not initialized. In C++, variables are not automatically initialized.
You must add:
int size = 0;
This is just a guess. In addition to the missing initialization of size, the following:
do{
.....
}
while (data[size].model.length() > 0);
looks also quite suspect to me: as soon as data[size].model has some content (which it does after the first read, this will evaluate to true and you probably have an infinite loop.
If you craft the for loop correctly, you don't need the do-while loop.