Dynamically allocate memory for vectors - c++

I am trying to replace arrays with vectors but I can't figure out how.
Replace this function to dynamically allocate memory for vectors:
string readFile(string filename, string** list, int size){
*list = new string[size];
ifstream file(filename);
string line;
for (int i = 0; i < size; i++){
getline(file, line);
*(*list + i) = line;
}
file.close();
return **list;
}
And here's my attempt to change it to vectors with no luck. Any feedback is greatly appreciated:
string processFile(string filename, vector<string>** list, int size){
*list = new vector<string>(size);
ifstream file(filename);
string line;
for (int i = 0; i < size; i++){
getline(file, line);
*list[i] = line; // error
}
file.close();
return **list; // error
}

You will need some proper error handling, but basically, you need neither pointers nor fixed sizes if you use containers:
std::vector<std::string> readLinesFromFile(const std::string& filename)
{
std::vector<std::string> result;
std::ifstream file(filename);
for (std::string line; std::getline(file, line); )
{
result.push_back(line);
}
return result;
}

There are several problems:
You don't need to use vector**, vector is equivalent to the list in previous code.
The return type is string, but you are returning vector**
This code should work, not tested though:
void processFile(string filename, vector<string>& list, int size){
//list = new vector<string>(size); // no need if you get a vector reference
ifstream file(filename);
string line;
for (int i = 0; i < size; i++){
getline(file, line);
list.push_back(line); //the error was because you are assigning string to a vector<string>*
}
file.close();
// you dont have to return, as vector is passed by reference
}
If you still need to use pointer of vector
void processFile(string filename, vector<string>** list, int size){
*list = new vector<string>(size); // bad practice
ifstream file(filename);
string line;
for (int i = 0; i < size; i++){
getline(file, line);
(*list)->push_back(line);
}
file.close();
// you dont have to return, as vector is passed by pointer
}

Change *list[i] = line to *list->push_back(line) and you should be okay for the first error.
The second error is going to depend on what your intent is for the return value. I think return *list->front(); will give the same result as the first example, but if you are planning on returning more than just the first line then you will need to do some concatenation. You can just create a local string and append each line as you read them.
Hopefully your teacher knows using new vector is almost always a code smell in C++ and is using this for a specific reason with a plan to fix it later.

here is a working example. enjoy :)
BTW - you don't need to pass the length, just instantiate the memory and use the push_back method.
#include <vector>
#include <fstream>
#include <string>
using namespace std;
void processFile(string filename, vector<string>** list, int size);
void main()
{
vector<string>* list = NULL;
processFile("C:\\temp.txt", &list, 13);
int i = 1;
}
void processFile(string filename, vector<string>** list, int size){
*list = new vector<string>();
ifstream file(filename);
string line;
for (int i = 0; i < size; i++){
getline(file, line);
(**list).push_back(line); // error
}
file.close();
}

Related

How to read from a text file in C++?

I am trying to read a bunch of strings from a text file and saving the characters in a 2D array. The following is my code:
char** fileReader(char* fileName){
ifstream treeFile;
treeFile.open(fileName);
string line;
vector<string> fileContents;
int rows=0, columns=0;
while (getline(treeFile, line )){
fileContents.push_back(line);
rows++;
}
columns = fileContents.at(0).length();
char** fileContentsArr;
fileContentsArr = new char*[rows];
for (int x=0; x < rows; x++){
fileContentsArr[x] = new char[columns];
for (int y=0; y < columns; y++){
fileContentsArr[x][y]= fileContents.at(x)[y];
}
}
treeFile.close();
return fileContentsArr;
}
Output should be:
TTTTTTTT
TTTTTTTT
TTTTFFFT
TTTTTTFF
FFFFTTFF
FFFFTTFF
FFFFTTTT
FFFFTTTF
But instead I am getting only the first 7 characters from each line and only the first 7 strings.
Actual output:
TTTTTTT
TTTTTTT
TTTTFFF
TTTTTTF
FFFFTTF
FFFFTTF
FFFFTTT
What am I doing wrong?
You can use the STL to do almost everything you need here:
vector<string> fileReader(char* fileName){
ifstream treeFile(fileName);
vector<string> fileContents(
(std::istream_iterator<string>(treeFile)),
std::istream_iterator<string>());
return fileContents;
}
This creates the vector using its two-iterator constructor, with the first iterator reading from treeFile and producing strings. The second iterator (default-constructed) signifies the end of the file.
Fixed version:
// Changed return type:
std::vector<std::string> fileReader(char* fileName){
ifstream treeFile;
treeFile.open(fileName);
string line;
std::vector<std::string> fileContents;
/// int rows=0, columns=0;
while (getline(treeFile, line )){
fileContents.push_back(line);
rows++;
}
// Cut off rest of code, instead:
return fileContents;
}
If you want to access a character, you can use
std::vector<std::string> data = fileReader("file.txt");
char value = data[3][2];
as intended.

std::vector subscript out of range while reading a file into the vector of strings in C++

I am new to c++. I am learning fast, but i dont know much yet.
I cannot see the problem with index in this function:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
void get_rows(string filepath, vector<string> &rows);
int main() {
vector<string> rows;
get_rows("ninja.txt", rows);
for (int i = 0; i < rows.size(); i++) {
cout << rows[i] << endl;
}
}
void get_rows(string filepath, vector<string> &rows) {
ifstream file;
file.open(filepath);
string str;
int index = 0;
while (!file.eof()) {
getline(file, str);
rows[index] = str;
index++;
}
}
Any help will be appreciated.
You have constructed an std::vector<string> object:
vector<string> rows;
and then later you are trying to access its elements although there are no elements in this vector yet:
rows[index] = str;
You should push new elements into the vector using push_back method:
rows.push_back(str);
Also note that using while (!file.eof()) is wrong becuase getline might fail inside the loop:
while (!file.eof()) {
getline(file, str);
...
}
Your loop should look the following way:
while (std::getline(file, str)) {
if (str.empty()) continue; // we skip empty lines
rows.push_back(str); // and push non-empty lines at the end
}
vector<string> rows;
^
size() is 0
get_rows("ninja.txt", rows);
void get_rows(string filepath, vector<string> &rows) {
//...
int index = 0;
rows[index] = str; // but there is no rows[0] yet
//...
}
you should either use push_back to add new elements into vector or create a vector with specified size at the beginning (if it is known)
vector<string> rows(160);
which has advantage over the former as you can avoid potential reallocation (which may invalidate pointers to vector elements i.e)

store the string file into dynamic array

I have three string file that will be stored into dynamic array, but I just try one of three file to test if this succed, so I'll do the same way to handle the three file i have.
the goal i'll shown the string that I get from the file to a ListView
this my code.
void __fastcall TFrmNewPeta::showDefaultRute() {
std::string lineDataAwal;
std::ifstream ifs_Awal;
int tempIndexAwal = 0;
ifs_Awal.open("DefaultDataAwal");
/*counting the line*/
while(std::getline(ifs_Awal,lineDataAwal)){++tempIndexAwal;}
/*use dynamic array to stored string*/
std::string *s = new std::string[tempIndexAwal];
for(int dx=0;dx<tempIndexAwal;dx++)
{
while(std::getline(ifs_Awal,lineDataAwal))
s[dx] = lineDataAwal[dx++];
}
for(int dex =0;dex<tempIndexAwal;++dex)
{
ItemDefult = ListView1->Items->Add();
ItemDefult->Caption = String(IntToStr(dex + 1));
ItemDefult->SubItems->Add(s[dex].c_str());
}
ifs_Awal.close();
delete []s;
s = NULL;
}
there's no errors during compile, but the result ListView just showing the number with this code ItemDefult->Caption = String(IntToStr(dex + 1));
can anyone show me how the best way for i do.
You are reading the file, leaving it open, and expecting to read it again. That won't work because the cursor in the file is at the end of the file (so your second while loop does nothing).
A much better approach would be:
std::vector<std::string> lines;
std::string line;
std::ifstream fin("Youfilename");
while (std::getline(fin, line))
{
lines.push_back(line);
}
fin.close();
// add data to your list view
its easier if you use std::vector for dynamic arrays and don't forget to first include the file header with #include<vector>
void __fastcall TFrmNewPeta::showDefaultRute() {
std::string lineDataAwal;
std::ifstream ifs_Awal;
std::vector<std::string> vec;
ifs_Awal.open("DefaultDataAwal");
/*get the string of lineDataAwal */
while(std::getline(ifs_Awal,lineDataAwal))
{ vec.push_back(lineDataAwal);}
for(int dex =0;dex<vec.size();++dex)
{
ItemDefult = ListView1->Items->Add();
ItemDefult->Caption = String(IntToStr(dex + 1));
ItemDefult->SubItems->Add(vec.at(dex).c_str());
}
ifs_Awal.close();
}
Hope this helps

C++ vector with pointer

I am stuck on a homework assignment. I have to read text from a file, allocate each word to memory, then user a pointer to send it to a vector<string*>. My program keeps overwriting the vector with the new word from the file instead of just adding it. I can't figure out why this is happening.
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
using namespace std;
void WordFunctions(string *pstr, vector<string*> &words)
{
words.push_back(pstr);
}
int main(){
ifstream file;
vector<string*> a;
string word;
int w =0;
file.open("word.txt");
while (!file.eof())
{
w++;
file >> word;
WordFunctions(&word, a);
}
file.close();
for (int i=0;i<10;i++){
cout<<(*a[i])<<" ";
delete a[i];
}
system ("pause");
}
Either use a vector<string> or allocate the new string on the heap:
void WordFunctions(string *pstr, vector<string*> &words)
{
words.push_back(new string(*pstr));
}
You are pushing the same element into vector which is the address of word. I massage a bit on your code
// pass reference to eliminate copy
void WordFunctions(string &str, vector<string> &words)
{
words.push_back(str);
}
int main(){
ifstream file;
vector<string> a; // you want to store string not the address of the string
string word;
int w =0;
file.open("words.txt");
while (!file.eof())
{
w++;
word.clear(); // clear the content before store something into it
file >> word;
WordFunctions(word, a);
}
file.close();
for (size_t i=0;i<a.size();i++){ // use size instead of hard code magic number
cout<<(a.at(i))<<" "; // use at function instead of []
}
system ("pause");
}
your word string has always the same address in memory, so in the loop you are changing the value of the string, but then you call WordFunctions passing to him always the same address.
If it's a constraint to use vector<string*> instead of vector<string>, you will likely need to allocate memory for new strings in the loop, copy there your word and then pass the new reference to WordFunctions
char *wordPtr
while (!file.eof())
{
w++;
file >> word;
wordPtr = (char *)malloc((strlen(word)+1)*sizeof(char));
strcpy(wordPtr, *word);
WordFunctions(wordPtr, a);
}

Heap corruption detected in dynamic 2 D array

I am getting heap corruption error while trying to free memory with delete
Here's code
char** split(char* inputstr, char delim, int& count){
char** ostr=NULL;
int numStr = 0;
int i=0,j,index=0;
while(inputstr[i]){
if(inputstr[i++]==delim)
numStr++;
}
if(inputstr[i-1]!=delim)
numStr++;
count= numStr;
ostr = new char*[numStr];
i=0;
while(inputstr[i])
{
j=i;
while(inputstr[j] && inputstr[j] != delim)
j++;
ostr[index] = new char[j-i+1];
//istr[j] = 0;
strncpy(ostr[index], inputstr+i,j-i);
ostr[index++][j-i]=0;
i=j+1;
}
return ostr;
}
for(int i=0,countStr;i<_numComp;i++){
char** _str = split(str[1+i],':',countStr);
message.lastTransList.cmpName[i] = new char[strlen(_str[0])+1];
strcpy(message.lastTransList.cmpName[i],_str[0]);
message.lastTransList.price[i] = atof(_str[1]);
for(int i=0; i<countStr;i++)
{
delete[] _str[i]; //this is working fine
_str[i] = 0;
}
delete[] _str; //exception is thrown at this line
}
I am not able to find the problem. Please help !
It's hard to see any error, there could be something wrong with your indexing that's causing a buffer overrun in the split function that's caught only when you try to delete the char** array.
How about converting to std::string and std::vectors like carlpett recommends (it's a good recommendation).
something like this:
void split(const std::string& str_, char delimiter_, std::vector<std::string>& result_)
{
std::string token;
std::stringstream stream(str_);
while( std::getline(stream, token, delimiter_) ) result_.push_back(token);
}
Then, you just call it with your string, delimiter and an empty std::vector and end up with a populated vector of substrings. You don't have to use new/delete and worry about the memory issues.