pull "firstword secondword" from text file into single char array - c++

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.

Related

How to read in a file and sort that file into different types of data all into specific arrays stored in a struct

I need to be able to read in (first number is meant to be id number followed by transactions, making bank statement code, d means deposit, w is withdrawal, numbers after are amount):
123 d45.10 d50.45 d198.56 w45.67
345 w34.00 d4.56 w45.13 d23.23 w23.12
639 d1000.34 d1234.56 w34.33 w345.87 w22.13
890 d345.67 d123.67 d45.99 d45.99 w34.77
666 d66.60 d666.66 d6.66 d66.6 d6666.66
and have it sort into different arrays all stored within a struct. I have tried string stream and various other things i thought of, I'm like medium versed in c++, in the first year class to be specific.
This is the code I have so far, The first set of read ins is working properly but I cannot get the second one to work:
#include<iostream>
#include<fstream>
#include<string>
#include<sstream>
using namespace std;
struct PersonAcct {
int acct_num[5];
string name[5];
double acct_bal[5];
};
struct PersonTrans {
int acct_num[5];
double trans[20];
char d_p[20];
};
int main() {
ifstream ifacc("accounts.txt");
PersonAcct p;
if (ifacc.is_open()) {
for (int i = 0; i <= 4; i++) {
ifacc >> p.acct_num[i];
ifacc >> p.name[i];
ifacc >> p.acct_bal[i];
}
}
ifacc.close();
ifstream iftrans;
iftrans.open("transactions.txt");
PersonTrans q;
string line,line2,line3,line4,line5;
if (iftrans.is_open()) {
int counter = 0;
while (getline(iftrans,line)) {
cout << line << endl;
counter++;
}
}
return 0;
}
Any help would be much appreciated! As I said before I am pretty new in retrospect to most of you on here so please be detailed or explain it for someone a bit daft in this subject, as I prob could be considered. I thank you and wish you a happy early holiday season!
If I understand you correct, you need to parse different words in different lines. You should be able to get this done easily with std::stringstream.
e.g.
...
iftrans.open("transactions.txt");
if (iftrans.is_open())
{
int counter = 0;
string line;
while (getline(iftrans, line)) // iterate through lines
{
int id;
string word;
stringstream ss(line);
ss >> id; // first word is the ID
while (ss >> word) // iterate though words of current line
{
switch (word[0]) // a word starts with either `w` or `d`
{
case 'w':
{
// remaining characters represent a valid double
double const val = stod(word.substr(1));
// process withdrawal
// ...
break;
}
case 'd':
{
double const val = stod(word.substr(1));
// process deposit
// ...
break;
}
default: break; // error?
}
}
counter++;
}
}
You may need to do additional error handling if the commented assumptions are not guaranteed to be valid always.
So I probably implemented it stupidly but here's what I thought you should do. Since the first thing will always be an ID I added that to acct_num;
Next I have a while loop for the line, I create a new string stream (Probably bad form, unsure) using a substring of everything after the first letter. Then I check the first letter of each transaction whether or not its a d or a w and put them into arrays. I had two counters for the w and d arrays. If they were vectors this wouldn't be necessary.
I wasn't sure what your original struct meant so I just created an array for deposits and withdrawals.
#include<iostream>
#include<fstream>
#include<string>
#include<sstream>
using namespace std;
struct PersonTrans {
int acct_num[5];
double deposits[20];
double withdrawals[20];
};
int main() {
ifstream iftrans;
iftrans.open("transactions.txt");
PersonTrans q;
if (iftrans.is_open()) {
int i = 0;
int d = 0;
int w = 0;
string line;
while (getline(iftrans,line)) {
stringstream in(line);
string tmp;
int idNumber;
in >> idNumber;
q.acct_num[i] = idNumber;
while(in >> tmp) {
double value;
std::stringstream transaction(tmp.substr(1));
transaction >> value;
if (tmp[0] == 'd'){
q.deposits[d] = value;
d++;
}
else if (tmp[0] == 'w'){
q.withdrawals[w] = value;
w++;
}
}
i++;
}
}
for(int i = 0; i < 20; i++){
cout << q.deposits[i] << endl;
}
return 0;
}

How to sort struct members under the same array index?

I am working on a C++ program, but I discovered that a function for sorting through arrayed struct members transferred from a text file didn't execute and ended up displaying the unsorted struct members.
This program is intended for my university course's semester project, where I made a basic ride-sharing program based on C++. The program must read the text file containing driver information and transfer it to arrayed structs, where it will then begin sorting from the lowest to highest price and display the sorted struct members. I did some research on a C++ textbook and even went on the a few forums to find similar problems, but I kept getting the same results as the text file was originally.
Here is the content of the text file for reference.
Annie Aliston
0174987723
Range Rover Evoque
60
6.00
Riley Winston
0174965739
Ford Everest
70
2.50
Here is my coding
#include <iostream>
#include <iomanip>
#include <fstream>
#include <string.h>
using namespace std;
struct ProSort
{
char nameProvider[10][40]; //40 character limit for nameProvider
char numPhoneProvider[10][11]; //11 character limit for numPhoneProvider
char nameVehicle[10][40]; //40 character limit for nameVehicle
double KMh[10];
double price[10];
};
ProSort sortingS[7]; //7 set of structs, but I'll put one of the said struct in the sorting function as an example below.
void sortS(ProSort, int);
void share_ride_sort_input(ProSort sortingS[], fstream& File)
{
File.open("sList/s4-Wheels.txt", ios::in);
{
if (File.is_open())
{
int a = 0;
while (!File.eof())
{
File >> ws;
File.getline(sortingS[0].nameProvider[a], 40);
File >> ws;
File.getline(sortingS[0].numPhoneProvider[a], 11);
File >> ws;
File.getline(sortingS[0].nameVehicle[a], 40);
File >> sortingS[0].KMh[a];
File >> sortingS[0].price[a];
//Contents of the text file will be assigned to the struct members above
a++; //Array index number will increase until the end of the text file
}
}
}
File.close();
}
void sortS(ProSort sortingS, int SIZE) //The sorting function for said issue above
{
int index;
int smallestIndex;
int location;
char temp[100];
double temp2;
for (index = 0; index < SIZE - 1; index++)
{
smallestIndex = index;
for (location = index + 1; location < SIZE; location++)
{
if (sortingS.price[index] > sortingS.price[smallestIndex])
{
smallestIndex = location;
strcpy(temp, sortingS.nameProvider[smallestIndex]);
strcpy(sortingS.nameProvider[smallestIndex], sortingS.nameProvider[index]);
strcpy(sortingS.nameProvider[index], temp);
strcpy(temp, sortingS.numPhoneProvider[smallestIndex]);
strcpy(sortingS.numPhoneProvider[smallestIndex], sortingS.numPhoneProvider[index]);
strcpy(sortingS.numPhoneProvider[index], temp);
strcpy(temp, sortingS.nameVehicle[smallestIndex]);
strcpy(sortingS.nameVehicle[smallestIndex], sortingS.nameVehicle[index]);
strcpy(sortingS.nameVehicle[index], temp);
temp2=sortingS.KMh[smallestIndex];
sortingS.KMh[smallestIndex]=sortingS.KMh[index];
sortingS.KMh[index]=temp2;
temp2=sortingS.price[smallestIndex];
sortingS.price[smallestIndex]=sortingS.price[index];
sortingS.price[index]=temp2;
// Basically all of the arrayed struct members with the same array index will move together as one whole set of driver info until every set of struct members is sorted
}
}
}
}
void share_ride_output(ProSort sortingS[], fstream& File) //Function for displaying the sorted struct members by writing to a text file.
{
File.open("sList/s4-Wheels-sorted.txt", ios::out);
{
if (File.is_open())
{
for(int i=0; i<2; i++)
{
File<<sortingS[0].nameProvider[i]<<endl;
File<<sortingS[0].numPhoneProvider[i]<<endl;
File<<sortingS[0].nameVehicle[i]<<endl;
File<<sortingS[0].KMh[i]<<" km/h"<<endl;
File<<"£"<<sortingS[0].charge[i]<<endl;
File<<"\n";
} //This is for writing 2 sets of struct members that was assigned in the share_ride_sort_input function to another text file.
}
}
File.close();
}
int main()
{
fstream File;
const int SIZE = 7;
share_ride_sort_input(sortingS, File);
for(int i=0; i<7; i++) //Originally this was meant for 7 car classes, but only the struct members from the s4-wheels.txt file will be put as an example
{
sortS(sortingS[i], SIZE);
}
share_ride_output(sortingS, File); //Sorted struct members will be written to a text file.
return 0;
}
I expect the output to the text file to be:
Riley Winston
0174965739
Ford Everest
70
2.50
Annie Aliston
0174987723
Range Rover Evoque
60
6.00
But instead, I got the output to be unsorted like this:
Annie Aliston
0174987723
Range Rover Evoque
60
6.00
Riley Winston
0174965739
Ford Everest
70
2.50
No error message is displayed, as the program runs without any warnings from the compiler. I would assume that I did something wrong in the sorting formula, but I couldn't seem to get other solutions to work either.
Main problem with your code is that actually it is not a C++. It is mostly C which is much harder to handle.
Second problem as someone has point out in comment, you reversed task hint. Instead doing array of structs you created an arrays inside a struct, which in this case made things even harder.
When you write C++ code, don't use C features like: char[] for strings (use std::string), C arrays SomeType variable[number] (use std::vector or std::array).
Start with something like that and the use std::sort and it will turn out quite easy:
struct Ride {
std::string dirver;
std::string phone;
std::string vehicle;
double distance;
double price;
};
std::istream& loadRide(std::istream& input, Ride& ride)
{
input >> std::ws; // consume white spaces in front
std::getline(input, ride.dirver);
std::getline(input, ride.phone);
std::getline(input, ride.vehicle);
return input >> ride.distance >> price;
}
std::istream& loadRides(std::istream& input, std::vector<Ride>& rides)
{
rides.clear();
Ride ride;
while(loadRide(input, ride)) {
rides.push_back(ride);
}
}
std::vector<Ride> loadRidesFromFile(const std::string& fileName)
{
std::ifstream f{ fileName };
std::vector<Ride> rides;
loadRides(f, rides);
return rides;
}

FIXED: Access Violation Reading Location (pointer to string array)

FIXED: http://pastebin.com/71QxqGk5
first post/question.
So this is C++ and I am trying to print an array of words.
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <cctype>
#include <ctime>
using namespace std;
//structs
struct Input
{
int size;
string* word;
bool is_palindrome[];
};
//prototypes
bool openInputFile(ifstream &ifs);
void File_to_Array(string* word, int &size);
void PrintArray(string* word, int size);
//main
int main()
{
Input myInput = { 0, nullptr, false };
File_to_Array(myInput.word, myInput.size);//copy arr and get size
cout << myInput.word; //this outputs 00000000
cout << *myInput.word; //this breaks and throws exception as commented below
//Exception thrown at 0x0098BB6B in Project1.exe: 0xC0000005: Access violation reading location 0x00000014.
PrintArray(myInput.word, myInput.size);//print array of strings
system("PAUSE");
return 0;
}
//functions
bool openInputFile(ifstream &ifs)
{
string filename;
cout << "Enter the input filename: " << endl;
getline(cin, filename);
ifs.open(filename.c_str());
return ifs.is_open();
}
void File_to_Array(string* word, int &size)//copies file to dyn arr and assigns size from first elem
{
ifstream myFile;
while (!openInputFile(myFile))
cout << "Could not open file" << endl;
string tempstr = "";
getline(myFile, tempstr);//first line is size of dyn arr
size = stoi(tempstr);//now we have max size of dyn arr of strings
word = new string [size];//now we have the array of strings, *word[index] = string1
int i;
for (i = 0; getline(myFile, word[i]) && i < size; ++i);//for each line
//copy line of string from file to string arr within "bool" test, second param of for loop //copying done
size = i;
myFile.close();//done with file, no need, close it
}
void PrintArray(string* word, int size)
{
//for (int i = 0; i < size; ++i)
//cout used to be here, but now its in main, for debugging
}
So I'm wondering if my problem is with passing a member of a struct, and if I should have instead passed the entire struct type "myInput" into the functions and use the -> operator to access the members of myInput.
below is an example of a text file
5
month
Runner
NEON
digit
ferret
nothing
the 5 would be the size of the dynamically allocated array, the rest are strings, as you can see there are 6 strings, so I have in the for loop a test for whether the file is still transferring strings to the array.
This part of the File_to_Array is causing the problem:
word = new string [size];
You think that you are setting the pointer of myInput object to point to the string array, but you're not. When you pass the pointer to the function here:
File_to_Array(myInput.word, myInput.size)
^^^^^^^^^^^^
you are really passing a copy of the pointer. So inside the File_to_Array, this copy is re-pointed to the newly-created string array, but the real pointer inside myInput is not changed. You should pass a reference to the pointer instead:
void File_to_Array(string*& word, int &size)
\___________/
^--reference to a pointer
I would also suggest you to use a vector[string] instead. Finally, your bool is_palindrome[]; member and it's initialization look very strange, but it's hard to comment further since they are never used in the code.

Reading number list from file to a dynamic array

I'm having trouble reading a number list from a .txt file to a dynamic array of type double. This first number in the list is the number of numbers to add to the array. After the first number, the numbers in the list all have decimals.
My header file:
#include <iostream>
#ifndef SORT
#define SORT
class Sort{
private:
double i;
double* darray; // da array
double j;
double size;
public:
Sort();
~Sort();
std::string getFileName(int, char**);
bool checkFileName(std::string);
void letsDoIt(std::string);
void getArray(std::string);
};
#endif
main.cpp:
#include <stdio.h>
#include <stdlib.h>
#include "main.h"
int main(int argc, char** argv)
{
Sort sort;
std::string cheese = sort.getFileName(argc, argv); //cheese is the file name
bool ean = sort.checkFileName(cheese); //pass in file name fo' da check
sort.letsDoIt(cheese); //starts the whole thing up
return 0;
}
impl.cpp:
#include <iostream>
#include <fstream>
#include <cstring>
#include <stdlib.h>
#include "main.h"
Sort::Sort(){
darray[0];
i = 0;
j = 0;
size = 0;
}
Sort::~Sort(){
std::cout << "Destroyed" << std::endl;
}
std::string Sort::getFileName(int argc, char* argv[]){
std::string fileIn = "";
for(int i = 1; i < argc;)//argc the number of arguements
{
fileIn += argv[i];//argv the array of arguements
if(++i != argc)
fileIn += " ";
}
return fileIn;
}
bool Sort::checkFileName(std::string userFile){
if(userFile.empty()){
std::cout<<"No user input"<<std::endl;
return false;
}
else{
std::ifstream tryread(userFile.c_str());
if (tryread.is_open()){
tryread.close();
return true;
}
else{
return false;
}
}
}
void Sort::letsDoIt(std::string file){
getArray(file);
}
void Sort::getArray(std::string file){
double n = 0;
int count = 0;
// create a file-reading object
std::ifstream fin;
fin.open(file.c_str()); // open a file
fin >> n; //first line of the file is the number of numbers to collect to the array
size = n;
std::cout << "size: " << size << std::endl;
darray = (double*)malloc(n * sizeof(double)); //allocate storage for the array
// read each line of the file
while (!fin.eof())
{
fin >> n;
if (count == 0){ //if count is 0, don't add to array
count++;
std::cout << "count++" << std::endl;
}
else {
darray[count - 1] = n; //array = line from file
count++;
}
std::cout << std::endl;
}
free((void*) darray);
}
I have to use malloc, but I think I may be using it incorrectly. I've read other posts but I am still having trouble understanding what is going on.
Thanks for the help!
Your use of malloc() is fine. Your reading is not doing what you want it to do.
Say I have the inputfile:
3
1.2
2.3
3.7
My array would be:
[0]: 2.3
[1]: 3.7
[2]: 0
This is because you are reading in the value 1.2 as if you were rereading the number of values.
When you have this line:
fin >> n; //first line of the file is the number of numbers to collect to the array
You are reading in the count, in this case 3, and advancing where in the file you will read from next. You are then attempting to reread that value but are getting the first entry instead.
I believe that replacing your while() {...} with the code below will do what you are looking for.
while (count != size && fin >> n)
{
darray[count++] = n; //array = line from file
std::cout << n << std::endl;
}
This should give you the correct values in the array:
[0]: 1.2
[1]: 2.3
[2]: 3.7
You appear to be writing the next exploitable program. You are mistakenly trusting the first line of the file to determine your buffer size, then reading an unlimited amount of data from the remainder of the file into a buffer that is not unlimited. This allows an evil input file to trash some other memory in your program, possibly allowing the creator of that file to take control of your computer. Oh noes!
Here's what you need to do to fix it:
Remember how much memory you allocated (you'll need it in step #2). Have a variable alleged_size or array_length that is separate from the one you use to read the rest of the data.
Don't allow count to run past the end of the array. Your loop should look more like this:
while ((count < alleged_size) && (cin >> n))
This both prevents array overrun and decides whether to process data based on whether it was parsed successfully, not whether you reached the end-of-file at some useless point in the past.
The less problematic bug is the one #bentank noticed, that you didn't realize that you kept your position in the file, which is after the first line, and shouldn't expect to hit that line within the loop.
In addition to this, you probably want to deallocate the memory in your destructor. Right now you throw the data away immediately after parsing it. Wouldn't other functions like to party on that data too?

Read two words into one char array element

I am trying to read in two words from text file into one char array element. I CANNOT USE std::string. I get a seg fault because my loop is looping in a way that goes out of bounds. I cannot copy in the two words. please help me do this!! My loop for card struct works perfectly. I cannot get the "first last" into the people[].name
// deck of cards
// below are initializations
#include
#include
#include
#include
#include
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[100];
int total;
card hand[4];
};
//program
int main()
{
//constants
char tempfName[100];
char templName[100];
//create struct array(s)
card deck[52];
card shuffledDeck[52];
player people[4];
//create pointers
card * deckPointer =NULL;
deckPointer = deck;
card * shuffledDeckPointer=NULL;
shuffledDeckPointer = shuffledDeck;
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);
//open file
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];
counter++;
}
}
}
it seems your tempfName is not pointing to a correct object in your while loop in else statement
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++;
}
You could do some cheating:
char full_name[256]; // Binary quantities are better.
snprintf(full_name, sizeof(full_name),
"%s %s",
first_name, last_name);
You should do more research regarding C-style string functions, like check a good reference book. It should have a chapter about the C-style string functions.
Here are some helpful ones:
strcpy, strcat, strchr, sprintf, strlen