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.
Related
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.
If it makes sense.
This is how it looks so far.
#include <iostream>
int main()
{
char *buffer;
buffer = new char[100];
std::cin >> buffer;
const int size = strlen(buffer);
char input[size];
delete buffer;
return 0;
}
I know I can use the string library but I'm trying to do without it.
I want to make the char size (in the code char input) depending on the input size.
The errors which I am getting is
expression did not evaluate to a constant
expression must have a constant value
on line 12 which is the
char input[size];
I know I can use the string library but I'm trying to do without it.
You can use std::vector<char>. If that is not an option either, allocate memory yourself and make sure that you deallocate the memory.
char* input = new char[size+1]; // Add an extra for the terminating null characterr
....
delete [] input;
For std::cin you need to pre-allocate a buffer. If you plan on using char[] and you really have to avoid strings you have to make sure you allocate enough memory.
Alternatively you can read char by char with scanf("%c",&newChar) until user inputs an escape character and allocate memory for array as you go.
I don't suppose you're limited to using iostream ?
If it is the length of used space in the buffer array you are trying to assign to size for the input array, there are two methods you can use...
#include <iostream>
using namespace std;
int main()
{
char *buffer = new char[100];
cin >> buffer;
int spaceUsed = 0;
for (unsigned int i = 0; i < 100; i++)
{
if (buffer[i] != '\0')
spaceUsed++;
}
char input[spaceUsed];
delete [] buffer;
return 0;
}
OR...
#include <iostream>
using namespace std;
int main()
{
char *buffer = new char[100];
cin >> buffer;
int spaceUsed = 0;
while (*buffer++)
spaceUsed++;
char input[spaceUsed];
delete [] buffer;
return 0;
}
Both code snippets do the exact same thing. Of course, using the string library would make your life much easier though.
I am reading through a file where each line represents an object I will add to an array. I do not know in advance how many lines are in the file, and I can only loop through it once. In addition, I am restricted to using plain arrays - no other container or collection classes. Here's what I've got:
ifstream f;
f.open("lines.csv");
string line;
string theCode;
string theName;
Manufacturer **manufacturers = new Manufacturer*[752]; // this number can't be here - I have to allocate dynamically
int index = 0;
while(getline(f, line))
{
theCode = line.substr(0, 6);
theName = line.substr(7, string::npos);
Manufacturer* theManufacturer = new Manufacturer(atoi(theCode.c_str()), theName);
manufacturers[index++] = theManufacturer;
}
You need to re-allocate a wider array every time when the index reaches the end of a current array. Like this.
int capacity = 752;
while(getline(f, line))
{
if (capacity <= index) {
capacity = (capacity+1) * 2;
Manufacturer **tmp = new Manufacturer*[capacity];
std::copy(manufacturers, manufacturers+index, tmp);
delete[] manufacturers;
manufacturers = tmp;
}
/* ... */;
}
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();
}
I allocated some memory (Word * wordList) for this struct:
struct Word{
int occurrences;
std::string wrd;
};
by writing:
Word * tempList = new Word[numWords + 1];
for(int i = 0; i < numWords; i++){
tempList[i] = wordList[i];
}
delete[] wordList;
wordList = tempList;
tempList = 0;
Word currWord = {1, wrd};
wordList[numWords] = currWord;
numWords++;
numWords is the size of wordList before and after this bit of code is called and wrd is a string passed into the method. This code runs to add a word when it isn't already present in wordList.
My problem is that when delete[] is called, the program stops working. I tried using delete to see what would happen and the program worked fine as far as I could tell. What is going on and why does delete[] cause my program to freeze?
wordList is a member of class WordsOfLength:
class WordsOfLength{
private:
int numWords;
Word * wordList;
public:
WordsOfLength();
WordsOfLength(int nNumWords, Word* nWordList);
~WordsOfLength();
void addWord(std::string wrd);
std::string getWord(int frequency);
friend void WordData::writeWordData(const char* fileName);
friend void WordData::setWordData(const char* fileName);
};
with constructor:
WordsOfLength::WordsOfLength(){
numWords = 0;
wordList = NULL;
}
and destructor:
WordsOfLength::~WordsOfLength(){
delete[] wordList;
wordList = 0;
}
If you have not allocated wordList anywhere before the problematic line then you are trying to dealocate unallocated memory.