Getting weird number when trying to read a number from file - c++

I'm trying to read a number from a text file, and I'm not allowed to use a binary file.
I've tried two methods to do this, and both return a strange result.
The first method:
char *theNumber;
int i = 0;
while(data>>text)
{
theNumber[i] = text;
i++;
}
returns some weird accented characters.
The second
int theNumber;
while(data>>text)
{
theNumber = text; // I tried theNumber<<text; as well
}
When I cout the result of this one it returns some big number when the text file contained 123.
string filename;
char text;
int p; //first prime number
int q; //second prime number
unsigned long long toBeEncrypted;
cout<<"Enter name of file to encrypt: ";
cin>>filename;
ifstream data;
ofstream encryptedData;
encryptedData.open("RSA_cipher.txt");
cout<<"Please enter two prime numbers:"<<endl;
p = getPrime(1);
q = getPrime(2);
//doing stuff with file
int theNumber;
data >> theNumber;
//int i = 0;
/*while(data>>text)
{
theNumber[i] = text;
i++;
}*/cout<<theNumber;
...//other stuff unrelated to the problem

This code:
char *theNumber;
int i = 0;
while(data>>text)
{
theNumber[i] = text;
i++;
}
Has Undefined Behavior, because you are using theNumber[i] to access an array which you haven't even allocated. You should have done:
char theNumber[255]; // Buffer size depends on the particular application
int i = 0;
while(data>>text)
{
theNumber[i] = text;  
i++;
}
The second attempt:
theNumber = text;
May or may not work, depending on how you defined text. This is impossible to answer without knowing the definition of text.
Anyway, if you want to read in a number from an input stream, just do:
int number;
data >> number;
UPDATE:
In the last code snippet you updated, the data stream is constructed, but never open. It is not associated to any file. Therefore, attempting to read from that stream won't succeed, and nothing will be stored into number (which is uninitialized).
ifstream data;
// data is not associated to any file after construction...
int theNumber;
data >> theNumber;

This does not create storage for your number.
char *theNumber;
It's a pointer. It points somewhere arbitrary, since you haven't assigned an address to it.
Try this.
char theNumber[10]; // Whatever size you need.
Or this.
int theNumber;

You didn't allocate any memory for char *theNumber;.
The theNumber points to a random location and you are printing random characters

Related

How do you use arrays of objects as parameters? C++

Im trying to pass an arrays of objects to a function so that it can be filled with the contents of a text file.
Update: Removed _menu[] from the function parameters and also from main. The code compiles just fine, but now it crashes right after openMenu asks for the cin to the filename.
code:
class Dish {
public:
int _itemNo;
string _category;
string _description;
double _price;
Dish();
Dish(int itemNo, string category, string description,
double price);
}
class DishDb{
private:
int _nElems;
public:
Dish _menu[20];
void openMenu(ifstream &fromFile);
};
void DishDb::openMenu(ifstream &fromFile){
string fileName;
int itemNo;
double price;
string description;
string category;
int numOfDishes = 0;
cout << "Enter file name: ";
cin >> fileName;
ifstream inFile(fileName);
do{
inFile >> itemNo;
_menu[numOfDishes]._itemNo = itemNo;
getline(inFile, category, ':');
_menu[numOfDishes]._category = category;
getline(inFile, description, ':');
_menu[numOfDishes]._description = description;
inFile >> price;
_menu[numOfDishes]._price = price;
numOfDishes++;
}while(!inFile.eof());
inFile.close();
cout << endl << "Menu was loaded.";
}
int main(){
string filename;
cout << "Enter today's transaction file name: ";
cin >> filename;
DishDb DDb;
ifstream inFile;
Dish _menu[20];
DDb.openMenu(inFile);
DDb.display();
return 0;
}
No errors for some reason
By default, arguments in C++ are passed by value.
By the phrasing of your question it seems like you are trying emulate pass-by-reference which is default in many other languages.
What you want to do in that case is have the function accept either a pointer (Dish* dishArr) or reference (Dish& dishArr) to the array. In either case, you can then use the pointer/reference to access the memory where the object resides from inside the function.
Note you will likely want to also pass in the size of the array so that you don't go out of the bounds of the array.
Pointer Example
void mutateDishArray(Dish* dishPtr, int numDishes) {
for(int i = 0; i < numDishes; ++i) {
dishPtr[i] = Dish(); // dereferencing the pointer using the array syntax
// this is equivalent to writing *(dishPtr+i) = Dish(); using the dereference operator '*'
}
}
int main() {
Dish dishArray[10]; // an array with memory for 10 dishes on the stack
mutateDishArray(dishArray, 10); // pass a pointer to the array (an array reference will decay into a pointer so we don't need the address-of operator '&')
}
That answers your question, but to fit your class layout, you may want to alter your code as follows.
You have a member definition for Dish _menu[20] array in the DishDb class, but you never initialize it with a constructor. Instead, you create a Dish _menu[20] array in main(). That array is outside the scope of the method DishDb::openMenu and not at all related to the Dish _menu[20] array defined in the Dish class.
class DishDb{
private:
int _nElems;
int _menuSize;
Dish* _menu;
public:
DishDb();
void openMenu(ifstream &fromFile);
};
DishDb::DishDb(Dish* _menu, int _menuSize)
: _nElems(0) // this is a 'member initializer-list'
, _menuSize(_menuSize)
, _menu(_menu)
{
}
Now, the DishDb constructor will accept a pointer to the array you had already made in main() and its member methods will have access to it through that pointer.
int main(){
string filename;
cout << "Enter today's transaction file name: ";
cin >> filename;
Dish _menu[20];
DishDb DDb(_menu, 20); // The DishDb is now constructed with a pointer to the _menu array on the stack
ifstream inFile;
DDb.openMenu(inFile);
DDb.display();
}
More on member initializer lists: https://en.cppreference.com/w/cpp/language/initializer_list
Here is a trivial example of using an array as a parameter for a function, for reference:
#include <iostream>
void func(int intArr[], unsigned int arrLength) {
for(unsigned int i = 0; i < arrLength; i++) {
std::cout << intArr[i] << '\n';
}
}
int main(int argc, char* argv[]) {
const unsigned int SIZE = 10;
int myInts[SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
func(myInts, SIZE);
return 0;
}
Behind the scenes, arrays are treated as pointers when passed into a function. The parameter int intArr[] could have been int *intArr with no change in effect. Incidentally, I see that you made your array of Dish objects a public variable, but you should really make it private because half the reason we encapsulate data within classes is to make sure that other code (that has no business altering our data) cannot change it directly. Instead, you should add a method that can update the array after performing its own checks and another method that can pass out a pointer to the array.
A couple other things to possibly revisit:
Class definitions must end with a semicolon, but you are missing one at the end of your class Dish definition.
!inFile.eof() is actually a really bad test to use to see if you have reached the end of a file. This is because inFile.eof() will only return true after you have attempted to read past the end of the file, but if you try to read past the end of a file you will get a segfault. Rather, you will want a loop condition that will guarantee that the next read from the file you perform is a valid read. You can accomplish this with a loop similar to:
while(inFile >> itemNo && getline(inFile, category, ':') && getline(inFile, description, ':') && inFile >> price){
_menu[numOfDishes]._itemNo = itemNo;
_menu[numOfDishes]._category = category;
_menu[numOfDishes]._description = description;
_menu[numOfDishes]._price = price;
numOfDishes++;
}
I would highly recommend reading this post on Stack Overflow for a better explanation about why this works better.
It looks like you're probably using using namespace std; somewhere outside of what you showed us, but this is considered bad practice. You won't always run into problems when using it, but as you go on to make larger projects the danger level goes up pretty quickly. This other post on Stack Overflow gives a good explanation of why.

Reading a text file into an array of integers

Given a file with some empty lines, some lines containing only integers, how would I make an array containing all of the integers? I have found methods for strings, but I need a list of integers. I want to do this using getline, but getline gives a string for "line"
A nonfunctioning example which returns the number of integers in the file and modifies a given array:
int getLinesFromFile(string fileName, int arr[], int arrLen) {
ifstream userFile;
userFile.open(fileName);
if (userFile.is_open()) {
int line;
int arrCount = 0;
while (getline(userFile, line)) {
if (tline.length() != 0 && arrCount < arrLen) {
arr[arrCount] = line;
arrCount++;
}
}
return arrCount;
}
else {
return -1;
}
userFile.close();
}
You can just use the >>-operator to read values. It will ignore any whitespace, including empty lines, between values. Here is a modified version of your function that uses it:
int getLinesFromFile(std::string fileName, int arr[], int arrLen) {
std::ifstream userFile(fileName);
int count = 0;
while(count < arrLen) {
int value;
userFile >> value;
if (!userFile.good())
return -1;
arr[count++] = value;
}
return count;
}
Note that you don't need to open and close the file manually, RAII will take care of that for you. Also, if the file could not be opened successfully, or if any other error occured while reading the file, userFile.good() will return false, so you can use that to detect and return an error. It's unclear if your function is supposed to read exactly arrLen values or if less is also valid. But at least you should take care not to write past the end of the provided array.

Truncating and removing characters from char array C++

I basically have a txt file that looks like this...
High Score: 50
Player Name: Sam
Number Of Kills: 5
Map
Time
I want to store everything before the : or whitespace after Map and Time into one array and everything after in another. For both Map and Time, there is nothing after and so I want to store the whitespace as null.
So far, I have managed to read and store all this information into a temp array. However, it is separating that I am having trouble with. This is my code:
istream operator >> (istream &is, Player &player)
{
char **temp;
char **tempNew;
char lineInfo[200]
temp = new char*[5];
tempNew = new char*[5];
for (int i=0; i<5; i++)
{
temp[i] = new char[200];
is.getline(lineInfo, sizeof(lineInfo));
int length = strlen(lineInfo);
for (int z=0; z < length; z++)
{
if(lineInfo[z] == '= ' ){ //HOW DO I CHECK IF THERE IS NOTHING AFTER THE LAST CHAR
lineInfo [length - (z+1)] = lineInfo [length];
cout << lineInfo << endl;
strncpy(temp[i], lineInfo, sizeof(lineInfo));
}
else{
tempNew[i] = new char[200];
strncpy(tempNew[i], lineInfo, sizeof(lineInfo));
}
}
}
If what you need is to find ':'
#include <cstring>
and just
auto occurance = strstr(string, substring);
Documentation here.
if occurance is not a null ptr, then see if occurance is at the end of the line from get line. If not, your value is everything after that :
Much easier with std::string.
// Read high score
int high_score;
my_text_file.ignore(10000, ':');
cin >> high_score;
// Read player name
std::string player_name;
my_text_file.ignore(10000, ':');
std::getline(my_text_file, player_name);
// Remove spaces at beginning of string
std::string::size_type end_position;
end_position = player_name.find_first_not_of(" \t");
if (end_position != std::string::npos)
{
player_name.erase(0, end_position - 1);
}
// Read kills
unsigned int number_of_kills = 0;
my_text_file.ignore(':');
cin >> number_of_kills;
// Read "Map" line
my_text_file.ignore(10000, '\n');
std::string map_line_text;
std::getline(my_text_file, map_line_text);
// Read "Text" line
std::string text_line;
std::getline(my_text_file, text_line);
If you insist on using C-style strings (arrays of char), you will have to use more complex and less safe functionality. Look up the following functions:
fscanf, strchr, strcpy, sscanf

pull "firstword secondword" from text file into single char array

I am getting a segmentation fault: core dumped error when I am reading in players file..
I am trying to add both "firstname lastname" to the player struct. I am trying to access the "0th" people and increment their name because i need both first and last, i cant simply fin >> people[i].name in a simply for loop as i do for the card value (not shown) "heart two 2" for example
// deck of cards
// below are initializations
#include <iostream>
#include <fstream>
#include <ctime>
#include <stdlib.h>
#include <string>
using namespace std;
//globals
const int maxCards = 52;
//Structs
struct card {
char suit[8];
char rank[6];
int cvalue;
char location;
};
struct player {
char name[];
int total;
card hand[];
};
//program
int main()
{
char tempfName[100];
char templName[100];
//create struct array(s)
card deck[52];
card shuffledDeck[52];
player people[4];
//set defalt values
for(int i=0;i<4;i++)
{
strcopy(people[i].name,"first last");
}
//open player names file
ifstream fin2;
string fin2Name;
//get file name from user
cout << "Enter player file name...(Players.txt)" << endl;
getline(cin,fin2Name);
fin2.open(fin2Name.c_str());
//check if Players.txt opens correctly
if(!fin2.good())
{
cout << "Error with player file!" << endl;
return 0;
}
else
{
int j =0;
//fin2 >> people[j].name; //prime file
while(fin2.good())
{
//find the length
int index =0, length=0;
while(tempfName[length] != '\0')
{
length++;
}
//now add space after first name
tempfName[length] = ' ';
length++;
while(templName[index] != '\0')
{
tempfName[length] = templName[index];
length++;
index++;
}
tempfName[length]='\0';
int counter =0;
while(templName[counter] != '\0')
{
people[0].name[counter] = templName[counter]; //testing program on "0th" people
counter++;
}
}
}
}
In your struct, name[] and hand[] are of undetermined size. It's hence difficult to read anything into them.
Then, once you've opened the stream, you're trying to determine the length of the unitianalized tempfName[]. This ain't no good: you're not sure it's null terminated and you'll go out of bounds ! This is the origin of your segfault.
Consider initalizing these by declaring them as:
char tempfName[100]{};
char templName[100]{};
Once this is fixed, your code still loops forever on while (fin2.good()) without reading anything, and bravely adding one whitespace to tempfName until you're out of bound.
Now suppose you'd fix all this, set a length to your name and undcomment your stream reading fin2 >> people[j].name; you'd still have a very risky situation: if the data would be longer that what you've foresseen, it would be truncated and the name wouldn't have a terminating '\0'.
Recommendation 1:
Consider using std::string instead of char[] whenever you consider storing a string. Example:
struct player {
string name = "first last" ; // initialisation value: no strcpy() needed !!
int total;
card hand[5]; // length ?
};
Recommendation 2:
Loop using your stream reading as loop condition:
while (fin2 >> people[j].name) { ///!!!
...
j++; // don't foget to increment your counter
}
However be carefull, because the >> will read one string at a time, the string ending at first whilespace (so only firstname).
If you adopt recommendation 1, it would be easy to write:
while (fin2 >> tempfName >> templName) { ///!!!
people[j++].name = tempfName + " " + templName;
}
which should perform pretty muchthe same thing that your loop, but with far less instructions and risks.
Recommendation 3:
If your number of players is fixed, define the max constant and use a for instead of a while to read your data:
const int max_player = 4;
player people[max_player];
...
for (j=0; j<max_player && (fin2 >> people[j].name); j++) // instead of the former while
If your limit of 4 was arbirary, consider using vectors. But that's another story for the moment.
Your struct player definition is not valid:
struct player {
char name[];
int total;
card hand[];
};
The C string fields name and hand need to have a length, e.g.
struct player {
char name[32];
int total;
card hand[32];
};
Your compiler should be giving you an error for this ("incomplete type").
Note also that since you are writing C++ code then it would be better to use std::string rather than C-style char * strings - it will be easier, more robust, and you won't be mixing C and C++ idioms.

C - Strange values while reading text file with fstream

I wrote a function to read a text file, create an array from the integer values in the file and return the reference of that array to main function. The code I wrote(in VS2010):
//main.cpp
void main(){
int T_FileX1[1000];
int *ptr=readFile("x1.txt");
for(int counter=0; counter<1000; counter++)
cout<<*(ptr+counter)<<endl;
}
and the function is:
//mylib.h
int* readFile(string fileName){
int index=0;
ifstream indata;
int num;
int T[1000];
indata.open("fileName");
if(!indata){
cerr<<"Error: file could not be opened"<<endl;
exit(1);
}
indata>>num;
while ( !indata.eof() ) { // keep reading until end-of-file
T[index]=num;
indata >> num; // sets EOF flag if no value found
index++;
}
indata.close();
int *pointer;
pointer=&T[0];
return pointer;
}
the data in the file contains positive numbers like
5160
11295
472
5385
7140
When I write each value in "readFile(string)" function, it writes true. But when I wrote it to screen as U wrote in "main" function, it gives values strangely:
0
2180860
1417566215
2180868
-125634075
2180952
1417567254
1418194248
32
2180736
irrelevant to my data. I have 1000 numbers in my file and I guess it raves these irrelevant values after a part of true writing. E.g. it writes first 500 values true, and then it writes irrelevant values to my data. Where is my fault?
int T[1000];
...
pointer=&T[0];
you are returning a pointer to a local stack variable which is going to get destructed.
I think what you want to do is to pass in the array T_FileX1 that you have defined to the function and use that directly to read the data into.
You return a pointer to the first element of an array which is allocated on the stack and gets destroyed after your function returns. Try using a vector instead:
vector<int> readFile(string fileName) {
ifstream indata;
int num;
vector<int> T;
indata.open("fileName");
if(!indata){
cerr<<"Error: file could not be opened"<<endl;
exit(1);
}
indata>>num;
while ( !indata.eof() ) { // keep reading until end-of-file
T.push_back(num);
indata >> num; // sets EOF flag if no value found
}
indata.close();
return T;
}
This is a case of undefined behavior. You return a pointer to a local variable, when the function returns the part of the stack used by the function is no longer valid.
Pass the array as an argument to the function instead.