Quick disclaimer, this is a contrived example meant to simulate an issue I am seeing in my homework problem. To that end, using strings is out of the question; I can only use char arrays as per the instructor :(
What I am trying to do is continuously read input from the keyboard and store it in a vector . The problem is, whatever data I add to the vector is lost as soon as the addData function ends (when I try to view it, I see \320\366\277_\377). I believe this is due to the fact I am using a vector<char*>, so the vector can only use the data for as long as the pointer exists. However, my code cannot compile if I change it to a vector<char>, as I get errors saying cannot convert char* to char.
So, how can I save a char array (not a single char) to a vector element? Or, is there perhaps a better approach to this that would avoid the problem altogether?
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
const int MAX_BUFFER_SIZE = 80;
// class declaration
class Example {
public:
void getData ();
void addData ( char * newData );
void displayData ();
private:
vector<char*> vec;
};
// main function
int main () {
bool quitProg;
int quit;
quitProg = false;
Example shoopDaWhoop; // buffers cannot overflow if you shoop da whoop
while (!quitProg) {
shoopDaWhoop.getData();
shoopDaWhoop.displayData();
cout << "Type 1 if you want to exit... ";
cin >> quit;
if (quit == 1) {
quitProg = true;
}
}
return 0;
}
void Example::getData () {
char userInput [MAX_BUFFER_SIZE];
cout << "Enter text: ";
cin.get(userInput, MAX_BUFFER_SIZE - 1, '\n');
if ( cin.fail() ) { // data is invalid
// clear and reset input stream
cin.clear(ios::goodbit);
cin.ignore(INT_MAX,'\n');
// alert user they entered bad data
cout << "That was bad data!" << endl;
}
else {
// data is good, pass it to addData
addData( userInput );
}
}
void Example::addData ( char * newData ) {
vec.push_back(newData);
cout << "You entered: " << vec.back() << endl;
}
void Example::displayData () {
for (int i=0; i<vec.size(); i++) {
cout << "Item " << i << ": " << vec[i] << endl;
}
}
Go with the vector<char>, but instead of
vec.push_back(newData);
Use:
size_t len = strlen(newData);
vec.insert(vec.end(), newData, newData + len);
Or does it actually need to be an vector of char arrays?
Use a std::vector<std::string>, that should "just work" with your existing code.
Since you cant do this with std::string (which would have been the proper way to use the language), the you a nested vector, like this:
std::vector<std::vector<char> > vex; // notice the space between the '>' chars, older compilers may need it this way
Then in your addData function:
std::vector<char> tmp;
while(*newData)
tmp.push_back(*newData++);
vec.push_back(tmp);
vector<char*> will only hold pointers - you want it to hold characters. To display the text, you'll have to iterate through the vector and print each character.
vector<char> vec;
void Example::addData ( char * newData ) {
cout << "You entered: ";
while (*newData) {
vec.push_back(*newData);
cout << (*newData);
++newData;
}
cout << endl";
}
If you want multiple strings you can use another vector.
Related
I am entirely new to programming so I'm sorry if I don't explain this well. For my C++ assignment I had to write an object-oriented program that reads the names from a text file (the text file is just a list of first names) and prints them to the console in alphabetical order using an array. Originally, the description of the assignment said that the file had 20 names, so I based my code around that. The program works, but now it turns out the assignment description was inaccurate and we shouldn't assume that the text file has a specific number of names. How do I convert my code from specifically reading 20 names to instead reading an undefined number of names, while still using an array?
I don't fully understand the concepts that I'm implementing so it's difficult for me to know how to change my code while still following the requirements of the assignment. Here is my code:
#include <iostream>
#include <algorithm>
#include <fstream>
#include <string>
using namespace std;
class Names
{
private:
ifstream inStudents;
string studentNames[20];
string name;
int j;
public:
Names();
~Names();
void openFile(string);
void testFile();
void readFile();
void sortNames();
void closeFile();
void display();
};
Names::Names()
{
}
Names::~Names()
{
}
void Names::openFile(string d)
{
inStudents.open(d);
}
void Names::testFile()
{
if (!inStudents)
{
cout << "File did not open" << endl;
exit(10);
}
}
void Names::readFile()
{
cout << "Reading the input file..." << endl;
int j = 0;
while (inStudents >> name && j < 20)
{
studentNames[j++] = name;
}
}
void Names::sortNames()
{
sort(studentNames, studentNames + 20);
}
void Names::closeFile()
{
inStudents.close();
}
void Names::display()
{
cout << endl << "The alphabetical list: " << endl << endl;
for (int i = 0; i<20; i++)
cout << studentNames[i] << " " << endl;
cout << endl << endl;
}
int main()
{
Names r;
r.openFile("students.txt");
r.readFile();
r.testFile();
r.sortNames();
r.display();
r.closeFile();
return 0;
}
You can use std::vector object instead of a regular array. It will look like that:
vector<string> studentNames;
Now, instead of using the following line to insert a name to a known place in the array:
studentNames[j++] = name;
use:
studentNames.push_back(name);
//or
studentNames.emplace_back(name);
The the while loop inside your readFile function, will look like this:
while (inStudents >> name)
{
studentNames.push_back(name);
}
To display it now, all you have to change in your display function is the range. The vector object include a function named size which returns you the current vector size, or in other words- the elements' count that the vector includes. It will seem like the following line:
for (int i = 0; i < studentNames.size(); i++)
The output of the code show gibberish values for all the variables of the Student struct. When the display function is ran.
I've include the relevant code in each of the add and display function for the binary file.
For the second function, does the seekg pointer automatically move to read the the next record each time the for loop runs?
//Student struct
struct Student
{
char name [30];
float labTest;
float assignments;
float exam;
};
//Writing function
afile.open(fileName,ios::out|ios::binary);
Student S;
strcpy(S.name,"test");
S.labTest = rand()%100+1;
S.assignments = rand()%100+1;
S.exam = rand()%100+1;
afile.write(reinterpret_cast<char*>(&S),sizeof(S));
afile.close();
//Reading function
afile.open(fileName,ios::in|ios::binary);
afile.seekg(0,ios::end);
int nobyte = afile.tellg();
int recno = nobyte / sizeof(Student);
Student S;
//Loop and read every record
for(int i = 0;i<recno;i++)
{
afile.read(reinterpret_cast<char*>(&S),sizeof(S));
cout << "Name of Student: " << S.name << endl
<< "Lab mark: " << S.labTest << endl
<< "Assignment mark: " << S.assignments << endl
<< "Exam mark: " << S.exam << endl << endl;
}
afile.close();
There are a lot of problems with your code:
Calling your write function will permanently overwrite the last written data set. You have to add: ios::append, so that new data will be written behind the last data you wrote before.
After you move with afile.seekg(0,ios::end); to get with tellg the file size, you have to go back to the start of the file before reading with afile.seekg(0,ios::beg)
It looks that you use a char array to store a string. This is not c++ style! And it is dangerous how you use it. If you use strcpy, you can copy a string which is longer than the space you reserved for it. So you should prefer std::string for that. But you can't simply write a struct which constains std::string as binary! To get checked copy you can use strncpy, but that is still not c++ ;)
For the second function, does the seekg pointer automatically move to read the the next record each time the for loop runs?
Yes, the file position moves which each successful read and write.
A general remark writing binary data by simply dumping memory content:
That is not a good idea, because you can only read that data back, if you use the same machine type and the same compiler options. That means: A machine with different endianness will read data totally corrupted. Also a different integer type ( 32 bit vs 64 bit ) will break that code!
So you should invest some time how to serialize data in a portable way. There are a lot of libraries around which can be used to read/write also complex data types like std::string or container types.
A hint using SO:
Please provide code which everybody can simply cut and paste and compiled. I did not know what your Student struct is. So I take a lot of assumptions! Is your struct really using char[]? We don't know!
#include <iostream>
#include <fstream>
#include <cstring>
const char* fileName="x.bin";
struct Student
{
char name[100]; // not c++ style!
int labTest;
int assignments;
int exam;
};
// Writing function
void Write()
{
std::ofstream afile;
afile.open(fileName,std::ios::out|std::ios::binary|std::ios::app);
Student S;
strcpy(S.name,"test"); // should not be done this way!
S.labTest = rand()%100+1;
S.assignments = rand()%100+1;
S.exam = rand()%100+1;
afile.write(reinterpret_cast<char*>(&S),sizeof(S));
afile.close();
}
void Read()
{
//Reading function
std::ifstream afile;
afile.open(fileName,std::ios::in|std::ios::binary);
afile.seekg(0,std::ios::end);
int nobyte = afile.tellg();
int recno = nobyte / sizeof(Student);
afile.seekg(0, std::ios::beg);
Student S;
//Loop and read every record
for(int i = 0;i<recno;i++)
{
afile.read(reinterpret_cast<char*>(&S),sizeof(S));
std::cout << "Name of Student: " << S.name << std::endl
<< "Lab mark: " << S.labTest << std::endl
<< "Assignment mark: " << S.assignments << std::endl
<< "Exam mark: " << S.exam << std::endl << std::endl;
}
afile.close();
}
int main()
{
for ( int ii= 0; ii<10; ii++) Write();
Read();
}
EDIT. Apparently, I was a bit too late in responding. Klaus has compiled a better, more comprehensive response dwelling into other problems regarding C-style char [], std::string and the endianness of the platform.
You should append to the file opened for every record. In your code you don't have this, at all. Please write the code in a way we can copy and paste, and test. As a working example, you should write some code that can be compiled and run as below:
#include <algorithm>
#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
// Student struct
struct Student {
char name[30];
float labTest;
float assignments;
float exam;
};
// Serializer
void serialize_student(const Student &s, const std::string &filename) {
// Append to the file, do not overwrite it
std::ofstream outfile(filename, std::ios::binary | std::ios::app);
if (outfile)
outfile.write(reinterpret_cast<const char *>(&s), sizeof(Student));
}
// Deserializer
std::vector<Student> deserialize_students(const std::string &filename) {
std::ifstream infile(filename, std::ios::binary);
std::vector<Student> students;
Student s;
while (infile.read(reinterpret_cast<char *>(&s), sizeof(Student)))
students.push_back(std::move(s));
return std::move(students);
}
int main(int argc, char *argv[]) {
// Generate records
std::vector<Student> mystudents;
std::generate_n(std::back_inserter(mystudents), 10, []() {
Student s;
std::strcpy(s.name, "test");
s.labTest = rand() % 100 + 1;
s.assignments = rand() % 100 + 1;
s.exam = rand() % 100 + 1;
return s;
});
// Print and write the records
for (const auto &student : mystudents) {
std::cout << student.name << ": [" << student.labTest << ','
<< student.assignments << ',' << student.exam << "].\n";
serialize_student(student, "students.bin");
}
// Read and print the records
auto records = deserialize_students("students.bin");
std::cout << "===\n";
for (const auto &student : records)
std::cout << student.name << ": [" << student.labTest << ','
<< student.assignments << ',' << student.exam << "].\n";
return 0;
}
Hi I am new to c++ and am having trouble understanding on how I would push and pop elements read from a text file to an array and displaying those elements in reverse order for example if i have a text file called hero.txt with elements Goku Luffy Naruto I would like the output to be Naruto Luffy Goku
this is what I have so far
string hero[100]; // array to store elements
int count=0;
int main()
{
fstream myfile;
string nameOffile;
string text;
string mytext;
cout << "Enter name of file" << endl;
cin >> nameOffile
myfile.open(nameOffile.c_str());
if (!myfile)
{
cerr << "error abort" << endl;
exit(1);
}
while (myfile >> text )
{
Push(mytext); //Note I know this is wrong I just don't know how to write it in a manner that will push the first element of the textfile to the top
}
myfile.close();
while(hero[count]=="")
{
//Again I know these two lines are incorrect just don't know how to implement in correct manner
cout <<hero[0] << " " <<endl;
Pop(mytext);
}
}
// Function for push
void Push(string mytext)
{
count = count + 1;
hero[count] = mytext;
}
void Pop(string mytext)
{
if(count=0)
{
mytext = " ";
}
else
{
mytext = hero[count];
count = count - 1;
}
}
Normally, a stack will begin with index = -1 to indicate that the stack is empty. So you need to replace
int count = 0
with
int count = -1
After you do all the pushing, your stack will look like this:
hero[0] = "Goku"
hero[1] = "Luffy"
hero[2] = "Naruto"
Now, to print it out in reverse order, you can just loop from the last index to the first. After pushing all the heroes string, count is now equal to 2. The last heroes will be at index = 0. So you can rewrite the loop as
while(count >= 0)
{
cout << hero[count] << " " <<endl;
Pop();
}
Your Pop function is also incorrect. In the if statement, you will replace the value of count to 0. What you need to do in Pop is just to decrement the value of count.
So you can rewrite it as
void Pop()
{
count = count - 1;
}
The vector class defined in the standard library acts like a stack.
For example:
// include the library headers
#include <vector>
#include <string>
#include <iostream>
// use the namespace to make the code less verbose
using namespace std;
int main()
{
// declare the stack
vector<string> heroStack;
// insert the elements
heroStack.push_back("Goku");
heroStack.push_back("Luffy");
heroStack.push_back("Naruto");
// print elements in reverse order
while(!heroStack.empty())
{
// get the top of the stack
string hero = heroStack.back();
// remove the top of the stack
heroStack.pop_back();
cout << hero << endl;
}
}
ok let's tart by improving your functions
push function works good but just change the order of it to be like this
void Push(string mytext)
{
hero[count] = mytext; //now you will start at index 0
count = count + 1;
}
pop function should be like this
you need to return a string value and you don't need to pass a parameter
string Pop()
{
if(count == 0)
{
return "";
}
else
{
count = count - 1;
mytext = hero[count];
return mytext;
}
}
now you are functions are ready let's use them
you are using the push function correctly in your main
we need to change the while which displays the output
it should be like this
while(true)
{
tempText = pop(); // this function will get you the last element and then remove it
if ( tempText == "" ) // now we are on top of the stack
break;
cout <<tempText << " " <<endl;
}
#include "stdafx.h"
#include <fstream>
#include <stack>
#include <string>
#include <iostream>
class ReadAndReversePrint
{
std::stack<std::string> st;
std::ifstream file;
public:
ReadAndReversePrint(std::string path)
{
file.open(path);
if (file.fail())
{
std::cout << "File Open Failed" << std::endl;
return;
}
std::string line;
while (!file.eof())
{
file >> line;
st.push(line);
}
file.close();
std::cout << "Reverse printing : " << std::endl;
while (!st.empty())
{
std::cout << st.top().c_str() << "\t";
st.pop();
}
std::cout << std::endl;
}
};
int main()
{
ReadAndReversePrint rrp("C:\\awesomeWorks\\input\\reverseprint.txt");
return 0;
}
I have the task to write a function char * stringReplace(const char * str, const char * what, const char * with) which replaces "what" with "with" on a new string with the correct length. So Inside the function I created dynamic array, which the function returns. But my question is how can I delete it after, as If i try to delete it in the main function, after i use it, it says it's undefined. Aren't dynamic arrays without a scope or I'm wrong?
Here's my code:
#include <iostream>
#include <string.h>
using namespace std;
bool areTheSame(const char * str, const char * what, unsigned p)
{
return areEqual;
}
unsigned howManyTimes(const char * str, const char * what)
{
}
char * stringReplace(const char * str, const char * what, const char * with)
{
}
int main()
{
char str[1000];
char what[1000];
char with[1000];
cout << "Enter your string\n";
cin.getline(str, 1000);
cout << "\nEnter \"what\" you want to replace\n";
cin.getline(what, 1000);
if (strlen(str) < strlen(what))
{
cout << "\"What\" contains more characters than the string!\n";
return 0;
}
cout << "\nEnter with what you want to replace it\n";
cin.getline(with, 1000);
cout << "\nYour string with replaced words looks like\n";
cout << stringReplace(str, what, with) << endl;
return 0;
}
P.S I deleted parts of the code as I found the answer I wanted and there is still time for the task, and I'm not sure I'm allowed to post my code publicly
You have to delete it in order to avoid memory leak. You can do something like this:
int main()
{
char str[1000];
char what[1000];
char with[1000];
char *replaced;
cout << "Enter your string\n";
cin.getline(str, 1000);
cout << "\nEnter \"what\" you want to replace\n";
cin.getline(what, 1000);
if (strlen(str) < strlen(what))
{
cout << "\"What\" contains more characters than the string!\n";
return 0;
}
cout << "\nEnter with what you want to replace it\n";
cin.getline(with, 1000);
cout << "\nYour string with replaced words looks like\n";
replaced = stringReplace(str, what, with);
cout << replaced << endl;
delete [] replaced;
return 0;
}
However note that this isn't the best pratice to split allocation/deallocation responsibility.
I have this code, but it won't compile and i can't understand what is wrong - i guess the pointering of the vector is not correct.
My idea was to collect some numbers in main() and store them in a vector and array, and then pass the memory address of them to a function, and using a pointers to print the data stored.
I came up with this when i read something about pointers which said that i should use them in order to save memory, so IMO the code below will not copy the contents of the vector and the array but use a pointer to access their location in memory - that's what i want to do.
#include <iostream>
#include <vector>
using namespace std;
void function(vector<int>* a, int *s)
{
cout << "function starts.." << endl;
for(int i=0;i<a->size();i++)
{
cout << a[i] << endl;
cout << s[a[i]] << endl;
}
cout << "function ends..." << endl;
}
int main(void)
{
vector<int> m;
int s[102];
for(int i=0;i<10;i++)
{
m.push_back(i*i);
s[i*i] = i-2;
}
function(&m, &s);
return 0;
}
I receive several errors on compiling, something is wrong.
Please tell me what's wrong with my code and how to fix it. thank you...
You should pass the vector by reference, not by pointer:
void function(vector<int>& a, int *s)
And then
function(m, ...);
Using [] on a pointer to a vector would certainly cause strange problems because it behaves as if a pointed to an array of std::vectors (while it actually only points to one). The vectors itself are never indexed by that. You could also use (*a)[...] to index the vector by the pointer.
if you insist in parsing by pointer then the correct syntax shoulld be:
void function(vector<int>* a, int *s[])
{
cout << "function starts.." << endl;
for(int i=0;i<a->size();i++)
{
cout << (*a)[i] << endl;
cout << (*s)[(*a)[i]] << endl;
}
cout << "function ends..." << endl;
}
First of all in the main program s is a pointer to an int, while m is a vector. Thus the function call should be as follows:
function(&m, s);
Secondly in the function a is a pointer to a vector, so should be indexed as follows: (*a)[i].
However you should really be using const references to pass your vector around:
void function(const vector& a, int *s)
{
..
cout << a[i] << endl;
..
}
And call it like:
function(m, s);
(corrected)
&s is in fact int(*)[102]: pointer to a pointer to an array of 102 items.
You should just say
function(&m, s);
This is because by old C legacy rule, an array is essentially a const pointer to its item with index 0. So s is already int*
This version works:
#include <iostream>
#include <vector>
using namespace std;
void function(const vector<int>& a, int s [102])
{
cout << "function starts.." << endl;
for(int i=0;i<(int)a.size();i++)
{
cout << a [i] << endl;
cout << s[a [i]] << endl;
}
cout << "function ends..." << endl;
}
int main(void)
{
vector<int> m;
int s[102];
for(int i=0;i<10;i++)
{
m.push_back(i*i);
s[i*i] = i-2;
}
function(m, s);
return 0;
}