This is content of my file txt:
1 Joey 1992
2 Lisa 1996
3 Hary 1998
And I have a struct:
struct MyStruct
{
int ID;
char *Name;
int Old;
};
I have a main () as this:
int main ()
{
MyStruct *List;
int Rows, Columns;
ReadFile (List, Rows, Columns, "file.txt");
return 0;
}
Now, I want to write a function ReadFile to get information from file txt and store into a List, beside store Rows and Colums:
void ReadFile (MyStruct *&List, int &Rows, int &Colums, char const *path)
{
// need help here
}
I know how to use ifstream to read integer from txt, but I don't know how to read substring, such as:
"Joey", "Lisa" and "Hary"
to store each into char *Name.
Please help me. Thanks so much !
You seem to work on old school exercises: you use arrays and c-string to store data elements, with all the hassle of manual memory management.
A first (old-school) approach
I'll use only very basic language features and avoid any modern C++ features
void ReadFile (MyStruct *&List, int &Rows, int &Colums, char const *path)
{
const int maxst=30; // max size of a string
Rows=0; // starting row
ifstream ifs(path);
int id;
while (ifs>>id) {
MyStruct *n=new MyStruct[++Rows]; // Allocate array big enough
for (int i=0; i<Rows-1; i++) // Copy from old array
n[i] = List[i];
if (Rows>1)
delete[] List; // Delete old array
List = n;
List[Rows-1].ID = id; // Fill new element
List[Rows-1].Name = new char[maxst];
ifs.width(maxst); // avoid buffer overflow
ifs>>List[Rows-1].Name; // read into string
ifs>>List[Rows-1].Old;
ifs.ignore(INT_MAX,'\n'); // skip everything else on the line
}
}
This assumes that List and Rows are uninitialized when the function is called. Note that Columns is not used here.
Note that you'll have to clean the mess when you no longer need the List: you have first to delete all the Name and then delete List.
How to do it in more modern C++
Nowadays, you'd no longer use char* but string:
struct MyStruct {
int ID;
string Name;
int Old;
};
And you wouldn't use an array for keeping all the items, but a container such as vector:
int main ()
{
vector<MyStruct> List;
ReadFile (List, "file.txt"); // no nead for Rows. It is replaced by List.size()
return 0;
}
And then you'd read it like this:
void ReadFile (vector<MyStruct>& List, string path)
{
ifstream ifs(path);
MyStruct i;
while (ifs>>i.ID>>i.Name>>i.Old) {
List.push_back(i); // add a new item on list
ifs.ignore(INT_MAX,'\n'); // skip everything else on the line
}
}
No worries about memory management; no worries about maximum size of strings.
Related
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;
}
I'm learning C++ and made myself a text file with over 10,000 lines. I'm trying to make a string array and insert the first line into the first array, the second line into the second array and so on. Here is what I've done so far:
ifstream theFile;
string inputFile;
cin >> inputFile;
theFile.open(inputFile.c_str());
const unsigned int ARRAY_CAP = 64U;
string line;
string *lineArr = new string[ARRAY_CAP];
if (theFile.is_open()) {
int lineNumber = 0;
while (!theFile.eof()) {
getline(theFile, line);
lineArr[i] = line;
i++;
}
}
A friend of mine told me to allocate the string array because I'm running out of memory, but I'm not even sure how to do that. How could I be able to allocate the string array?
If you want to stay with dynamically allocated arrays, you will need to expand them dynamically.
unsigned int lines_read = 0U;
std::string text_line;
unsigned int capacity = 4U;
std::string * p_array = new std::string[capacity];
while (std::getline(theFile, text_line))
{
p_array[lines_read] = text_line;
++lines_read;
if (lines_read > capacity)
{
// Allocate new array with greater capacity.
unsigned int old_capacity = capacity;
capacity = capacity * 2U;
std::string p_new_array = new std::string[capacity];
std::copy(p_array, p_array + old_capacity, p_new_array);
delete [] p_array;
p_array = p_new_array;
}
}
The std::vector performs similar memory management for you, so you don't have to do the above.
I have a Movie class which has constructor takes 7 parameter like this;
Movie:: Movie(string ttle,string sts ,double prc,int yr,string gnr,Date rls,int id)
I would like to use dynamic memory for an movie array but it gives error and i could not find it
int main() {
int counter=0; // size of array
Movie *moviearray;
moviearray= new Movie[counter];
ifstream filein("DVD_list.txt");
for (string line; getline(filein, line); )
{
counter++;
vector<string> v;
split(line, '\t', v); // v is an vector and puts string words that has splitted based on tab
moviearray[counter] =(v[0],v[1] ,stod(v[2]),stoi(v[3]),v[4],Date(v[5]),stoi(v[6])); // ERROR
How can I create an movie object in that array?
This:
int counter=0; // size of array
moviearray= new Movie[counter];
Does not make sense. You are allocating an array of zero objects. Later you use it. This is illegal.
Instead, try:
std::vector<Movie> movies;
Then in your loop:
movies.push_back(Movie(v[0],v[1] ,stod(v[2]),stoi(v[3]),v[4],v[5],stoi(v[6])));
As in the title, I need to add user-specified number of spaces at the beginning of some word, using array of chars. I need to do it in a function which takes my array as a parameter and returns it. Here's my code:
#include <iostream>
using namespace std;
void writeDownCharArray(char t[], int sizee)
{
for (int i=0;i<sizee;i++)
{
cout<<t[i];
}
}
char * addSpaces(char t[], int ammountOfSpaces)
{
int numberOfCharacters=0;
for (int i=0; t[i]!=NULL; i++){numberOfCharacters++;} //checking the amount of characters in my array
char t2[numberOfCharacters+10];
for (int i=0; i<ammountOfSpaces; i++) {t2[i]=' ';} //adding the sapces
for (int i=ilosc;i<numberOfCharacters+ammountOfSpaces;i++) {t2[i]=t[i-ammountOfSpaces];} //filling my new array with characters from the previous one
return t2;
}
int main()
{
int numberOfSpaces;
char t[10];
cout << "Text some word: ";
cin.getline(t,10);
cout<<"How many spaces?: ";cin>>numberOfSpaces;
writeDownCharArray(addSpaces(t, numberOfSpaces), HERE);
return 0;
}
And now: How do I print it to the screen? If I say cout<<addSpaces(t, numberOfSpaces); it actually prints something strange to the screen (not numbers, just strange characters). And if I say writeDownCharArray, then what should I put in "HERE" place?
The C++ way to solve this would be to use a std::string like
std::string add_spaces(const std::string & line, std::size_t number_of_spaces)
{
std::string spaces(number_of_spaces, ' ');
return spaces + line;
}
If you cannot use std::string then you are doing to have to deal with dynamic memory allocations and change
char t2[numberOfCharacters+10];
to
char * ts = new char[numberOfCharacters + ammountOfSpaces + 1];
We have to have this as Variable length arrays are not standard and trying to return a pointer to an array declared in a function will leave you with a dangling pointer and trying to use it is undefined behavior.
Since new[] was used in the function you will need to remember to call delete[] on the pointer that is returned after you are done with it. This is another benefit of using a std::string as it takes care of itself.
As far as writeDownCharArray is concerned you do not need a size parameter as cout can handle null terminated c-strings. You can simply have
void writeDownCharArray(char t[])
{
cout<<t;
}
And then you main would look like
char * foo = addSpaces(t, numberOfSpaces);
writeDownCharArray(foo);
delete [] foo;
I have a class with 2 data members: size and an array of ints (dynamically allocated). The purpose of the class is to create an array of a size and fill it with values. The task is to create a constructor that takes a string as its parameter, but the string looks like this: "12|13|14|15" etc. I have searched this but all the solutions are a little too complicated, as they involve vectors and we haven't started with vectors yet. I basically want to put these numbers into the array of ints, 1 by 1 and also find out the size of the array. How can I do that? I tried messing with getline and stringstream but that gave me a load of errors. My code looks like this.
#include <string>
#include <iostream>
#include <sstream>
using namespace std;
class IntArrays {
private:
static int objcount;
int size;
public:
int *arrayints;
const static int objcountf();
IntArrays(int);
IntArrays(const IntArrays &p){
size = p.size;
for (int i = 0;i <size;i++){
arrayints[i] = p.arrayints[i];
}
}
IntArrays(std::string f){
// ignore the other constructors, this is the constructor that is giving me trouble
int counter =0;
istringstream inStream(f);
string newstring;
while (getline(iss,newstring, '|')){
arrayints[counter] = stoi(newstring);
counter++;}
void enternums();
(note that this is only the header file, and that the current string constructor I have there does not work.
This code is my version. I prefer to use a vector rather than a raw array.
Class definition:
class IntArrays {
public:
IntArrays(const string&, const char&);
const vector<int>& data() { return _data; }
const int size() { return _data.size(); }
private:
vector<int> _data;
};
The following is the constructor implementation:
IntArrays::IntArrays(const string& str, const char& delimiter) {
string buff;
for(auto& n:str) {
if(n != delimiter) buff+=n; else
if(n == delimiter && buff != "") {
_data.push_back(stoi(buff));
buff = "";
}
}
if(buff != "") _data.push_back(stoi(buff));
}
And then we just use the class:
IntArrays a("1|4|9|6|69", '|');
vector<int> da = a.data();
IntArrays b("1,4,9,6,69", ',');
vector<int> db = b.data();
I will try to have a recursion for that =p
Sorry that I cannot provide a c++ version =p..
This is a java version i guess.
list parse(string data, list base) {
if (data.length > 0) {
string s = data.subStr(0,1);
if (s == "|") {
base.push(0); //set the initial value for a new value
} else {
int i = parseInt(s);
int result = base.pop()*10 + i; //recalculate the result
base.push(result);
}
return parse(data.subStr(1),base); //recursion
} else {
return base; //return the result
}
}
As Joachim pointed out, you do not initialize the pointer. Unfortunately, you do not have the size of the array before the allocation, so you are left with a few solutions:
process the input twice (really bad if you have a large number of entries); On the first pass, count the inputs. Then allocate the array, then read them again, into the allocated array.
read the inputs into a linked list; Each element in the list would hold a value, and the address of the next element.
Preallocate a block of memory and hope it is large enough to read the entire array. If it is not, reallocate a larger block and copy the already read values into it, then discard the initial block (this is what std::vector does).