Reading in a file that has strings and ints in c++ - c++

So I have a sample file that I would like to read in, looking something like:
data 1
5
data 2
0
9
6
6
1
data 3
7
3
2
I basically want to assign each of these to variables I have in a struct, eg. my struct looks like:
struct sample_struct
{ int data1;
double* data2;
double* data3;
};
How do I approach this question?
I think I would be able to do it if I had the sample number of integers following each of the string titles, but like this I have no idea. Please help.

You have to solve 2 problems.
Dynamic memory management.
Detect, where, inwhich section we are and where to store the data.
Number 1 will usually be solved with a std::vector in C++. Raw pointers for owened memory or C-Style arrays are not used in C++.
If you do not want to or are not allowed to the a std::vetor you need to handcraft some dynamic array. I made an example for you.
For the 2nd part, we can simple take the alphanumering string as a separator for different sections of the source file. So, if we see an alpha character, we go to a new section and then store the data in the appropriate struct members.
Input and output in C++ is usually done with the extractor >> and inserter << operator. This allows to read from and write to any kind of stream. And, in C++ we use often object oriented programming. Here, data and methods are packed in one class/struct. Only the class/struct should know, how to read and write its data.
Based on the above thoughts, we could come up with the below solution:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <cctype>
// Ultra simle dynamic array
struct DynamicDoubleArray {
// Constructor and Destructor
DynamicDoubleArray() { values = new double[capacity]; }; // Allocate default memory
~DynamicDoubleArray() { delete[] values; }; // Release previously allocated memory
// Data
double* values{}; // Here we store the values. This is a dynamic array
int numberOfElements{}; // Number of elements currently existing in dynamic array
int capacity{ 2 }; // Number of elements that could be stored in the dynamic array
void push_back(double v) { // Add a new aelement to our dynamic array
if (numberOfElements >= capacity) { // Check, if we have enough capacity to store the new element
capacity *= 2; // No, we have not. We need more capacity
double* temp = new double[capacity]; // Get new, bigger memory
for (int k = 0; k < numberOfElements; ++k) // Copy old data to new bigger memory
temp[k] = values[k];
delete[] values; // Delete old data
values = temp; // And assign new temp data to our original pointer
}
values[numberOfElements++] = v; // Store new data and increment element counter
}
};
// Our sample struct
struct SampleStruct {
// Data part
int data1{};
DynamicDoubleArray data2{};
DynamicDoubleArray data3{};
// Help functions. We overwrite the inserter and extractor operator
// Extract elements from whatever stream
friend std::istream& operator >> (std::istream& is, SampleStruct& s) {
std::string line{}; // Temporaray storage to hold a complete line
int section = 0; // Section. Where to store the data
while (std::getline(is, line)) {
if (std::isalpha(line[0])) { // If we see an alpha character then we are in the next section
++section; // Now, we will use the next section
continue;
}
switch (section) { // Depending on in which section we are
case 1:
s.data1 = std::stoi(line); // Section 1 --> Store int data
break;
case 2:
s.data2.push_back(std::stod(line)); // Section 2 --> Add/Store double data 2
break;
case 3:
s.data3.push_back(std::stod(line)); // Section 3 --> Add/Store double data 2
break;
default:
std::cerr << "\nError: internal mode error\n";
break;
}
}
return is;
}
// Simple output
friend std::ostream& operator << (std::ostream& os, const SampleStruct& s) {
os << "\nData 1: " << s.data1 << "\nData 2: ";
for (int k = 0; k < s.data2.numberOfElements; ++k)
os << s.data2.values[k] << ' ';
os << "\nData 3: ";
for (int k = 0; k < s.data3.numberOfElements; ++k)
os << s.data2.values[3] << ' ';
return os;
}
};

Related

Read 2d array from txt file in c++ [duplicate]

This question already has answers here:
Characters duplicate in a multidimensional array
(3 answers)
Closed 1 year ago.
This is my code
#include<bits/stdc++.h>
using namespace std;
int main()
{
char arr1[10][10];
cout << "Reading Start" << endl;
ifstream rfile("test.txt");
rfile.getline(arr1[10], 10);
int i, j;
for (i = 0; i < 6; i++)
{
for (j = 0; i < 6; j++)
{
cout << arr1[i][j];
}
}
cout << "\nRead Done" << endl << endl;
rfile.close();
}
This is my test.txt file
0 4 7 0 0 0
4 0 0 5 3 0
7 0 0 0 6 0
0 5 3 0 0 2
0 3 4 0 0 2
0 0 0 2 2 0
I want to read this matrix but when using the above code then it shows core dumped output, can anyone give me a better solution to do this thing?
can anyone give me a better solution to do this thing?
A better alternative would be to use a 2D vector as shown below. The advantage of using a vector over an array is that you don't need to specify(know) the rows and columns beforehand. That is, the text input file can have as many rows and columns and there is no need to ask the user(or preallocate) how many rows and columns does the file have. std::vector will take care of it as shown below.
The below program uses a 2D std::vector for storing information(like integers values in this case) in 2D manner. After reading all the values from the file you can process the vector according to your needs. The program shown reads data(int values) from input.txt and store those in a 2D vector. Also, this program works even if there are uneven number of columns. You can use the below program as a reference(starting point).
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include<fstream>
int main() {
std::string line;
int word;
std::ifstream inFile("input.txt");
//create/use a std::vector instead of builit in array
std::vector<std::vector<int>> vec;
if(inFile)
{
while(getline(inFile, line, '\n'))
{
//create a temporary vector that will contain all the columns
std::vector<int> tempVec;
std::istringstream ss(line);
//read word by word(or int by int)
while(ss >> word)
{
//std::cout<<"word:"<<word<<std::endl;
//add the word to the temporary vector
tempVec.push_back(word);
}
//now all the words from the current line has been added to the temporary vector
vec.emplace_back(tempVec);
}
}
else
{
std::cout<<"file cannot be opened"<<std::endl;
}
inFile.close();
//now you can do the whatever processing you want on the vector
//lets check out the elements of the 2D vector so the we can confirm if it contains all the right elements(rows and columns)
for(std::vector<int> &newvec: vec)
{
for(const int &elem: newvec)
{
std::cout<<elem<<" ";
}
std::cout<<std::endl;
}
return 0;
}
The output of the above program can be seen here. The input file through which int values are read is also given at the above mentioned link.
Advantages of using vector
You don't need to ask the user for the number of rows and columns in the input file. That is you don't have to fix(hardcode) the size of your array.
The above program works even if there are uneven entries in any particular row.
std::vector takes care of memory management for you. So you don't have to use new and delete by yourself which needs more attention/care.(in case you're thinking of creating array on heap)
Because there are so many possible solutions, let us just show some of them.
The basic difference is:
If we know the dimensions of the array at compile time, so, if there are compile time constants, then we can still use a C-Style array, or better, a std::array.
If we do not know the dimensions of the source data array, then we need a dynamic container that can grow, for example a std::vector.
In all cases, we can use the index operator [] with a standard for loop or a range based for loop with references. There is no difference.
Examples:
C-Style array with standard for loops and index based access
#include <iostream>
#include <fstream>
constexpr int NumberOfRows = 6;
constexpr int NumberOfColumns = 6;
int main() {
// Open the sourcefile
std::ifstream sourceFileStream{ "test.txt" };
// And check, if it could be opened
if (sourceFileStream) {
// Define 2D array to hold all data and initialize it with all 0
char array2D[NumberOfRows][NumberOfColumns]{};
// Read the rows and columns from the source file
for (int row = 0; row < NumberOfRows; ++row)
for (int col = 0; col < NumberOfColumns; ++col)
sourceFileStream >> array2D[row][col];
// Debug output
for (int row = 0; row < NumberOfRows; ++row) {
for (int col = 0; col < NumberOfColumns; ++col) std::cout << array2D[row][col] << ' ';
std::cout << '\n';
}
}
else std::cerr << "\nError: Could not open source file\n\n";
}
C-Style array with range based for loop and reference access
#include <iostream>
#include <fstream>
constexpr int NumberOfRows = 6;
constexpr int NumberOfColumns = 6;
int main() {
// Open the sourcefile
std::ifstream sourceFileStream{ "test.txt" };
// And check, if it could be opened
if (sourceFileStream) {
// Define 2D array to hold all data and initialize it with all 0
char array2D[NumberOfRows][NumberOfColumns]{};
// Read the rows and columns from the source file
for (auto& row : array2D)
for (auto& col : row)
sourceFileStream >> col;
// Debug output
for (const auto& row : array2D) {
for (const auto& col : row) std::cout << col << ' ';
std::cout << '\n';
}
}
else std::cerr << "\nError: Could not open source file\n\n";
}
C++ std::array with range based for loop
#include <iostream>
#include <fstream>
#include <array>
constexpr int NumberOfRows = 6;
constexpr int NumberOfColumns = 6;
int main() {
// Open the sourcefile
std::ifstream sourceFileStream{ "test.txt" };
// And check, if it could be opened
if (sourceFileStream) {
// Define 2D array toholdall data and initialize it with all 0
std::array<std::array<char, NumberOfColumns>, NumberOfRows> array2D{};
// Read the rows and columns from the source file
for (auto& row : array2D)
for (auto& col : row)
sourceFileStream >> col;
// Debug output
for (const auto& row : array2D) {
for (const auto& col : row) std::cout << col << ' ';
std::cout << '\n';
}
}
else std::cerr << "\nError: Could not open source file\n\n";
}
Dynamic solution, with a std::vector
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
int main() {
// Open the sourcefile
std::ifstream sourceFileStream{ "test.txt" };
// And check, if it could be opened
if (sourceFileStream) {
// Define 2D array to hold all data and initialize it with all 0
std::vector<std::vector<char>> array2D{};
// Read the rows and columns from the source file
std::string line{};
while (std::getline(sourceFileStream, line)) {
// Add a new row to our matrix
array2D.push_back(std::vector<char>{});
// Read all column data
char c{};
for (std::istringstream iss(line); iss >> c; array2D.back().push_back(c))
;
}
// Debug output
for (const auto& row : array2D) {
for (const auto& col : row) std::cout << col << ' ';
std::cout << '\n';
}
}
else std::cerr << "\nError: Could not open source file\n\n";
}
More modern and compact C++ solution
#include <vector>
#include <string>
#include <iterator>
int main() {
// Open the sourcefile and check, if it could be opened
if (std::ifstream sourceFileStream{ "test.txt"}; sourceFileStream) {
// Define 2D array to hold all data and initialize it with all 0
std::vector<std::vector<char>> array2D{};
// Read the rows and columns from the source file
for (std::string line{}; std::getline(sourceFileStream, line);) {
std::istringstream iss(line);
array2D.push_back({ std::istream_iterator<char>(iss), {} });
}
// Debug output
for (const auto& row : array2D) {
for (const auto& col : row) std::cout << col << ' ';
std::cout << '\n';
}
}
else std::cerr << "\nError: Could not open source file\n\n";
}
And now, we imagine that we do not have any vector or even a string.
For that, we build a small class "DynamicArray" with some functions and an iterator. This can easily be extended.
And the result will be that in main, only one small statement, sourceFileStream >> dada; will read all the data into a 2d array.
Please note. We are only using stream functions for stream io, nothing more.
Cool . . .
#include <iostream>
#include <sstream>
#include <fstream>
// The Dynamic Array has an initial capacity.
// If more elements will be added, there will be a reallocation with doublecapacity
constexpr unsigned int InitialCapacity{ 4 };
// Definition of simple dynamic array class
template <typename T>
class DynamicArray {
protected:
// Internal data ------------------------------------------------------------------------------
T* data{}; // Dynamic Storage for Data
unsigned int numberOfElements{}; // Number oe elements currently in the container
unsigned int capacity{ InitialCapacity }; // Current maximum capacity of the container
public:
// Construction and Destruction ---------------------------------------------------------------
DynamicArray() { data = new T[capacity]; } // Default constructor. Allocate new memory
DynamicArray(const DynamicArray& other) { // Copy constructor. Make a deep copy
capacity = numberOfElements = other.numberOfElements;
data = new T[capacity]; // Get memory, same size as other container
for (size_t k = 0; k < other.numberOfElements; ++k)
data[k] = other.data[k]; // Copy data
}
~DynamicArray() { delete[] data; } // Destructor: Release previously allocated memory
bool empty() { return numberOfElements == 0; }
void clear() { numberOfElements = 0; }; // Clear will not delete anything. Just set element count to 0
void push_back(const T& d) { // Add a new element at the end
if (numberOfElements >= capacity) { // Check, if capacity of this dynamic array is big enough
capacity *= 2; // Obviously not, we will double the capacity
T* temp = new T[capacity]; // Allocate new and more memory
for (unsigned int k = 0; k < numberOfElements; ++k)
temp[k] = data[k]; // Copy data from old memory to new memory
delete[] data; // Release old memory
data = temp; // And assign newly allocated memory to old pointer
}
data[numberOfElements++] = d; // And finally, stor the given fata at the end of the container
}
// Add iterator properties to class ---------------------------------------------------------------
// Local class for iterator
class iterator{
T* iter{}; // This will be the iterator
public: // Define alias names necessary for the iterator functionality
using iterator_category = std::input_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = T*;
using reference = T&;
explicit iterator(T* i) : iter(i) {}; // Default constructor for the iterator
T operator *() const { return *iter; } // Dereferencing
iterator& operator ++() { ++iter; return *this; } // Pre-Increment
bool operator != (const iterator& other) { return iter != other.iter; } // Comparison
};
// Begin and end function to initiliaze an iterator
iterator begin() const { return iterator(data); }
iterator end() const { return iterator (data + numberOfElements); }
// Operators for class------------------------ ---------------------------------------------------------------
T& operator[] (const size_t i) { return data[i]; } // Index operator, get data at given index. No boundary chek
DynamicArray& operator=(const DynamicArray& other) { // Assignment operator. Make a deep copy
if (this != &other) { // Prevent self-assignment
delete[] data; // Release any previosly existing memory
capacity = numberOfElements = other.numberOfElements;// Take over capacity and number of elements from other container
data = new int[capacity]; // Get new memory, depending on size of other
for (unsigned int k = 0; k < numberOfElements; ++k) // Copy other data
data[k] = other.data[k];
}
return *this;
}
// Extractor and Inserter ------------------------ ---------------------------------------------------------------
friend std::istream& operator >> (std::istream& is, DynamicArray& d) {
std::stringstream ss{};
for (char c{}; (is.get(c) and c != '\n'); ss << c); // Read one line until newline into a stringstream
for (T x{}; ss >> x; d.push_back(x)); // Now extract the data from there
return is;
}
friend std::ostream& operator << (std::ostream& os, const DynamicArray& d) {
for (unsigned int k = 0; k < d.numberOfElements; ++k) // Ultra simple output
os << d.data[k] << ' ';
return os;
}
};
// -----------------------------------------------------------------------------------------------------------
// Very simple 2d array. Derived from standard dynamic array and just defining differen input and output
template <typename T>
class Dynamic2dArray : public DynamicArray<DynamicArray<T>> {
public:
friend std::istream& operator >> (std::istream& is, Dynamic2dArray& d) {
for (DynamicArray<T> temp{}; is >> temp; d.push_back(temp), temp.clear());
return is;
}
friend std::ostream& operator << (std::ostream& os, const Dynamic2dArray& d) {
for (unsigned int k = 0; k < d.numberOfElements; ++k)
os << d.data[k] << '\n';
return os;
}
};
// -----------------------------------------------------------------------------------------------------------
int main() {
// Open the sourcefile and check, if it could be opened
if (std::ifstream sourceFileStream{ "test.txt" }; sourceFileStream) {
// Define 2D array to hold all data and initialize it with all 0
Dynamic2dArray<int> dada;
// Read complete matrix from file
sourceFileStream >> dada;
// Debug output. Show complete Matrix
std::cout << dada;
}
else std::cerr << "\n\nError. Could not open source file\n\n";
}
Basically, everything is the same, somehow . . .
There are a lot of other ways to perform the specific task, but i guess your method is not wrong, and you have just made a simple typing mistake in your second for loop condition. so ill just fix your code for you.
and also you could just input single values at a time as u go.
#include<bits/stdc++.h>
using namespace std;
int main()
{
char arr1[10][10];
cout <<"Reading Start" <<endl;
ifstream rfile("test.txt");
int i,j;
for(i=0;i<6;i++){
for(j=0;j<6;j++){
rfile >> arr1[i][j];
cout << arr1[i][j] << " ";
}
cout << endl;
}
cout <<"\nRead Done" <<endl<<endl;
rfile.close();
}
Output :

C++ Spell checking program with two classes; Dictionary and word

Here is the specification for the code:
You are to use the Word and Dictionary classes defined below and write all member functions and any necessary supporting functions to achieve the specified result.
The Word class should dynamically allocate memory for each word to be stored in the dictionary.
The Dictionary class should contain an array of pointers to Word. Memory for this array must be dynamically allocated. You will have to read the words in from the file. Since you do not know the "word" file size, you do not know how large to allocate the array of pointers. You are to let this grow dynamically as you read the file in. Start with an array size of 8, When that array is filled, double the array size, copy the original 8 words to the new array and continue.
You can assume the "word" file is sorted, so your Dictionary::find() function must contain a binary search algorithm. You might want to save this requirement for later - until you get the rest of your program running.
Make sure you store words in the dictionary as lower case and that you convert the input text to the same case - that way your Dictionary::find() function will successfully find "Four" even though it is stored as "four" in your Dictionary.
Here is my code so far.
#include <cstring>
#include <iostream>
#include <fstream>
using namespace std;
class Word
{
char* word_;
public:
Word(const char* text = 0);
~Word() { delete[] word_; word_ = nullptr; }
const char* word() const;
};
Word::Word(const char* arg)
: word_(new char[strlen(arg) + 1])
{
strcpy(word_, arg);
}
const char* Word::word() const
{
return word_;
}
class Dictionary
{
Word** words_;
unsigned int capacity_; // max number of words Dictionary can hold
unsigned int numberOfWordsInDictionary_;
void resize() {
capacity_ = capacity_ * 2;
cout << "Size = " << capacity_ << endl;
};
void addWordToDictionary(char* word) { words_ += *word; };
public:
Dictionary(const char* filename);
~Dictionary() {
delete[] words_; words_ = nullptr;
};
bool find(const char* word);
};
Dictionary::Dictionary(const char * filename)
: words_(new Word*[8]), capacity_(8), numberOfWordsInDictionary_(0)
{
ifstream fin(filename);
if (!filename) {
cout << "Failed to open file!" << endl;
}
char buffer[32];
while (fin.getline(buffer, sizeof(buffer)))
{
if (numberOfWordsInDictionary_ == capacity_)
{
resize();
}
addWordToDictionary(buffer);
}
}
bool Dictionary::find(const char * left)
{
int last = capacity_ - 1,
first = 0,
middle;
bool found = false;
while (!found && first <= last) {
middle = (first + last) / 2;
if (strcmp(left, reinterpret_cast<char*>(words_[middle])) == 0) {
found = true;
}
else if (left > reinterpret_cast<char*>(words_[middle]))
last = middle - 1;
else
first = middle + 1;
}
return found;
}
;
bool cleanupWord(char x[] ) {
bool lower = false;
int i = 0;
while (x[i]) {
char c = x[i];
putchar(tolower(c));
lower = true;
}
return lower;
}
int main()
{
char buffer[32];
Dictionary Websters("words.txt");
ifstream fin("gettysburg.txt");
cout << "\nSpell checking " << "gettysburg.text" << "\n\n";
while (fin >> buffer) {
if (cleanupWord(buffer) == true) {
if (!Websters.find(buffer)) {
cout << buffer << " not found in the Dictionary\n";
}
}
}
system("PAUSE");
}
When I run the program it stops after outputting "spellchecking Gettysburg.txt" and I don't know why. Thank you!
The most likely cause of this problem is the text files have not been opened. Add a check with is_open to make sure they have been opened.
When using Relative Paths (any path that does not go all the way back to the root of the file system (and is an Absolute Path)), take care that the program is being run from the directory you believe it to be. It is not always the same directory as the executable. Search Term to use to learn more about this: Working Directory.
Now on to other reasons this program will not work:
void addWordToDictionary(char* word) { words_ += *word; };
is not adding words to the dictionary. Instead it is advancing the address at which words_ points by the numeric value of the letter at *word. This is extremely destructive as it loses the pointer to the buffer allocated for words_ in the constructor making delete[] words_; in the Dictionary destructor ineffective and probably fatal.
Instead you want to (Note I use want to with a bit of trepidation. What you really want to do is use std::vector and std::string, but I strongly suspect this would upset the assignment's marker)
Dynamically allocate a new Word with new.
Place this word in a free spot in the words_ array. Something along the lines of words_[numberOfWordsInDictionary_] = myNewWord;
Increase numberOfWordsInDictionary_ by 1.
Note that the Words allocated with new must all be released in the Dictionary destructor. You will want a for loop to help with this.
In addition, I would move the
if (numberOfWordsInDictionary_ == capacity_)
{
resize();
}
from Dictionary to addWordToDictionary so that any time addWordToDictionary is called it is properly sized.
Hmmm. While we're at it, let's look at resize
void resize() {
capacity_ = capacity_ * 2;
cout << "Size = " << capacity_ << endl;
};
This increases the object's capacity_ but does nothing to allocate more storage for words_. This needs to be corrected. You must:
Double the value of capacity_. You already have this.
Allocate a larger buffer to hold the replacement of words_ with new.
Copy all of the Words in words_ to the larger buffer.
Free the buffer currently pointed to by words_
Point words_ at the new, larger buffer.
Addendum
I haven't looked closely at find because the carnage required to fix the reading and storage of the dictionary will most likely render find unusable even if it does currently work. The use of reinterpret_cast<char*> is an alarm bell, though. There should be no reason for a cast, let alone the most permissive of them all, in a find function. Rule of thumb: When you see a reinterpret_cast and you don't know what it's for, assume it's hiding a bug and approach it with caution and suspicion.
In addition to investigating the Rule of Three mentioned in the comments, look into the Rule of Five. This will allow you to make a much simpler, and probably more efficient, dictionary based around Word* words_, where words_ will point to an array of Word directly instead of pointers to Words.

How to speed up loading text file to multi vector

I have to load large files (several GB) with data and I want to load them to two dimensional vector. Code below does the job, but it is insanely slow. To be more specific, the goal is to get all lines where values in 2nd column are equal to index(_lh,_sh). And then exclude the lines where 4th column value is same as line+1 and line-1.
Now, I'am new to c++ and I usualy code in Python (have working code for this problem already). But I need it to be as fast as posible so I tried to rewrite my python code to C++. But it rus slower than Python now (and only getting the data to vector is implemented)... so before I proceed, I want to improve that.
From what I have found in similar questions, the problem would be dynamic vectors, .push_back() and getline().
I am rather confused about maping and chunk loading mentioned in similar questions so I am not able to change the code acording to these.
Could you help me to optimize this code?
Thank you.
#include <iostream>
#include <sstream>
#include <fstream>
#include <array>
#include <string>
#include <vector>
using namespace std;
int pixel(int radek, int sloupec, int rozmer = 256) {
int index = (radek - 1) * rozmer + sloupec;
int index_lh = (index - rozmer - 1);
int index_sh = (index - rozmer);
int index_ph = (index - rozmer + 1);
int index_l = (index - 1);
int index_p = (index + 1);
int index_ld = (index + rozmer - 1);
int index_sd = (index + rozmer);
int index_pd = (index + rozmer + 1);
array<int, 9> index_all = { {index, index_lh, index_sh, index_ph, index_l, index_p, index_ld, index_sd, index_pd } };
vector<vector<string>> Data;
vector<string> Line;
string line;
for (int m = 2; m < 3; m++) {
string url = ("e:/TPX3 - kalibrace - 170420/ToT_ToA_calib_Zn_" + to_string(m) + string(".t3pa"));
cout << url << endl;
ifstream infile(url);
if (!infile)
{
cout << "Error opening output file" << endl;
system("pause");
return -1;
}
while (getline(infile, line))
{
Line.push_back(line);
istringstream txtStream(line);
string txtElement;
vector<string> Element;
while (getline(txtStream, txtElement, '\t')){
Element.push_back(txtElement);
}
Data.push_back(Element);
}
}
cout << Data[1][0] << ' ' << Data[1][1] << ' ' << Data[1][2] << endl;
return 0;
}
int main()
{
int x = pixel(120, 120);
cout << x << endl;
system("pause");
return 0;
}
Vectors can get slow if their underlying buffer gets reallocated often. A vector is required to be implemented on a buffer of continuous memory, and every time the buffer limit is exceeded, it will have to allocate a new and larger buffer, and then copy the content from the old buffer to the new buffer. If you have an idea of how big buffers you require (you don't need to be excact), you can help the program to allocate a buffer of appropriate size by using e.g. Data.reserve(n) (where n is approximately the number of elements you think you need). This does note change the "size" of the vector, just the size of the underlying buffer. As a concluding remark, I have to say I haven't really ever benchmarked this, so this may or may not improve the performance of your program.
EDIT: Though, I deem it a bit more likely that the performance is a bit bottled by the line Data.push_back(Element); which makes a copy of the Element-vector. If you're using C++11, I believe it's possible to work around this by doing something like Data.emplace_back(std::move(Element)); in which case you can't alter Element afterwards (it's content is moved). You would also need to include memory for std::move.
In the while loop, you could try changing the lines from
while (getline(infile, line))
{
Line.push_back(line);
istringstream txtStream(line);
string txtElement;
vector<string> Element;
while (getline(txtStream, txtElement, '\t')){
Element.push_back(txtElement);
}
Data.push_back(Element);
}
to:
while (getline(infile, line))
{
Line.push_back(line);
istringstream txtStream(line);
string txtElement;
//vector<string> Element; [-]
Data.emplace_back(); // [+]
while (getline(txtStream, txtElement, '\t')) {
//Element.push_back(txtElement); [-]
Data.back().push_back(txtElement); // [+]
}
//Data.push_back(Element); [-]
}
That way, the vectors in Data don't need to get moved or copied there -- they are already constructed, albeit empty. The vectors in Data are default-constructed with .emplace_back(). We get the last element in Data with the .back() function, and push our values as usual with .push_back(). Hopefully this helps :)
You can try using old C file reading API (FILE*, fopen(), etc.) or setting a bigger buffer for std::istringstream as follows
constexp std::size_t dimBuff { 10240 } // 10K, by example
char myBuff[dimBuff];
// ...
istringstream txtStream(line);
txtStream.rdbuf()->pubsetbuf(myBuff, dimBuff);
Another thing that you can try is using std::deques instead of std::vectors (but I've no idea if this is useful).
As suggested by muos, you can use move semantics; you can use emplace_back() also.
So I suggest to try with
Element.push_back(std::move(txtElement));
Data.push_back(std::move(Element));
or
Element.emplace_back(std::move(txtElement));
Data.emplace_back(std::move(Element));
You can also swith the following lines (there isn't a move constructor from a string for std::istringstream, if I'm not wrong)
Line.push_back(line);
istringstream txtStream(line);
adding move semantics (and emplace_back())
istringstream txtStream(line);
Line.emplace_back(std::move(line));
p.s.: obviously reserve() is usefull
You can also use reserve(int) on the vectors so they are created closer to the target size.
That too can avoid a lot of vector hopping around the heap, as the vector will only be recreated of it passes the target size.
You can call reserve again if vector passes the size you previously reserved:
vector<int> vec;
vec.reserve(10);
for (int i=0;i < 1000; i++)
{
if ( vec.size() == vec.capacity() )
{
vec.reserve(vec.size()+10);
}
vec.push_back(i);
}

C++ Declaring arrays in class and declaring 2d arrays in class

I'm new with using classes and I encountered a problem while delcaring an array into a class. I want to initialize a char array for text limited to 50 characters and then replace the text with a function.
#ifndef MAP_H
#define MAP_H
#include "Sprite.h"
#include <SFML/Graphics.hpp>
#include <iostream>
class Map : public sprite
{
private:
char mapname[50];
int columnnumber;
int linenumber;
char casestatematricia[];
public:
void setmapname(char newmapname[50]);
void battlespace(int column, int line);
void setcasevalue(int col, int line, char value);
void printcasematricia();
};
#endif
By the way I could initialize my 2d array like that
char casestatematricia[][];
I want later to make this 2d array dynamic where I enter a column number and a line number like that
casestatematricia[linenumber][columnnumber]
to create a battlefield.
this is the cpp code so that you have an idea of what I want to do.
#include "Map.h"
#include <SFML/Graphics.hpp>
#include <iostream>
using namespace sf;
void Map::setmapname(char newmapname[50])
{
this->mapname = newmapname;
}
void Map::battlespace(int column, int line)
{
}
void Map::setcasevalue(int col, int line, char value)
{
}
void Map::printcasematricia()
{
}
thank you in advance.
Consider following common practice on this one.
Most (e.g. numerical) libraries don't use 2D arrays inside classes.
They use dynamically allocated 1D arrays and overload the () or [] operator to access the right elements in a 2D-like fashion.
So on the outside you never can tell that you're actually dealing with consecutive storage, it looks like a 2D array.
In this way arrays are easier to resize, more efficient to store, transpose and reshape.
Just a proposition for your problem:
class Map : public sprite
{
private:
std::string mapname;
int columnnumber;
int linenumber;
std::vector<char> casestatematricia;
static constexpr std::size_t maxRow = 50;
static constexpr std::size_t maxCol = 50;
public:
Map():
casestatematricia(maxRow * maxCol, 0)
{}
void setmapname(std::string newmapname)
{
if (newmapname.size() > 50)
{
// Manage error if you really need no more 50 characters..
// Or just troncate when you serialize!
}
mapname = newmapname;
}
void battlespace(int col, int row);
void setcasevalue(int col, int row, char value)
{
// check that col and line are between 0 and max{Row|Column} - 1
casestatematricia[row * maxRow + col] = value;
}
void printcasematricia()
{
for (std::size_t row = 0; row < maxRow; ++row)
{
for (std::size_t col = 0; col < maxCol; ++col)
{
char currentCell = casestatematricia[row * maxRow + col];
}
}
}
};
For access to 1D array like a 2D array, take a look at Access a 1D array as a 2D array in C++.
When you think about serialization, I guess you want to save it to a file. Just a advice: don't store raw memory to a file just to "save" time when your relaunch your soft. You just have a non portable solution! And seriously, with power of your computer, you don't have to be worry about time to load from file!
I propose you to add 2 methods in your class to save Map into file
void dump(std::ostream &os)
{
os << mapname << "\n";
std::size_t currentRow = 0;
for(auto c: casestatematricia)
{
os << static_cast<int>(c) << " ";
++currentRow;
if (currentRow >= maxRow)
{
currentRow = 0;
os << "\n";
}
}
}
void load(std::istream &is)
{
std::string line;
std::getline(is, line);
mapname = line;
std::size_t current_cell = 0;
while(std::getline(is, line))
{
std::istringstream is(line);
while(!is.eof())
{
char c;
is >> c;
casestatematricia[current_cell] = c;
++current_cell;
}
}
}
This solution is only given for example. They doesn't manage error and I have choose to store it in ASCII in file. You can change to store in binary, but, don't use direct write of raw memory. You can take a look at C - serialization techniques (just have to translate to C++). But please, don't use memcpy or similar technique to serialize
I hope I get this right. You have two questions. You want know how to assign the value of char mapname[50]; via void setmapname(char newmapname[50]);. And you want to know how to create a dynamic size 2D array.
I hope you are comfortable with pointers because in both cases, you need it.
For the first question, I would like to first correct your understanding of void setmapname(char newmapname[50]);. C++ functions do not take in array. It take in the pointer to the array. So it is as good as writing void setmapname(char *newmapname);. For better understanding, go to Passing Arrays to Function in C++
With that, I am going to change the function to read in the length of the new map name. And to assign mapname, just use a loop to copy each of the char.
void setmapname(char *newmapname, int length) {
// ensure that the string passing in is not
// more that what mapname can hold.
length = length < 50 ? length : 50;
// loop each value and assign one by one.
for(int i = 0; i < length; ++i) {
mapname[i] = newmapname[i];
}
}
For the second question, you can use vector like what was proposed by Garf365 need to use but I prefer to just use pointer and I will use 1D array to represent 2d battlefield. (You can read the link Garf365 provide).
// Declare like this
char *casestatematricia; // remember to initialize this to 0.
// Create the battlefield
void Map::battlespace(int column, int line) {
columnnumber = column;
linenumber = line;
// Clear the previous battlefield.
clearspace();
// Creating the battlefield
casestatematricia = new char[column * line];
// initialise casestatematricia...
}
// Call this after you done using the battlefield
void Map::clearspace() {
if (!casestatematricia) return;
delete [] casestatematricia;
casestatematricia = 0;
}
Just remember to call clearspace() when you are no longer using it.
Just for your benefit, this is how you create a dynamic size 2D array
// Declare like this
char **casestatematricia; // remember to initialize this to 0.
// Create the battlefield
void Map::battlespace(int column, int line) {
columnnumber = column;
linenumber = line;
// Clear the previous battlefield.
clearspace();
// Creating the battlefield
casestatematricia = new char*[column];
for (int i = 0; i < column; ++i) {
casestatematricia[i] = new char[line];
}
// initialise casestatematricia...
}
// Call this after you done using the battlefield
void Map::clearspace() {
if (!casestatematricia) return;
for(int i = 0; i < columnnumber; ++i) {
delete [] casestatematricia[i];
}
delete [][] casestatematricia;
casestatematricia = 0;
}
Hope this help.
PS: If you need to serialize the string, you can to use pascal string format so that you can support string with variable length. e.g. "11hello world", or "3foo".

C++: Program crash while adding object to custom vector class

I'm working on an email validation program for my cmpsci class and am having trouble with this one part.
What I'm doing is reading a list of valid top level domains from a text file into a vector class I wrote myself (I have to use a custom vector class unfortunately). The problem is that the program reads in and adds the first few domains to the vector all well and fine, but then crashes when it gets to the "org" line. I'm completely stumped why it works for the first few and then crashes.
Also, I have to use a custom string class; that's why I have the weird getline function (so I get the input in a char* for my String constructor). I've tried using the standard string class with this function and it still crashed in the same way so I can rule out the source of the problem being my string class. The whole program is quite large so I am only posting the most relevant parts. Let me know if more code is needed please. Any help would be awesome since I have no clue where to go from here. Thanks!
The ReadTlds function:
void Tld::ReadTlds() {
// Load the TLD's into the vector
validTlds = Vector<String>(0); // Init vector; declaration from header file: "static Vector<String>validTlds;"
ifstream in(TLD_FILE);
while(!in.eof()) {
char tmpInput[MAX_TLD_LENGTH]; // MAX_TLD_LENGTH equals 30
in.getline(tmpInput, MAX_TLD_LENGTH);
validTlds.Add(String(tmpInput)); // Crashes here!
}
}
My custom vector class:
#pragma once
#include <sstream>
#define INIT_CAPACITY 100
#define CAPACITY_BOOST 100
template<typename T> class Vector {
public:
// Default constructor
Vector() {
Data=NULL;
size=0;
capacity=INIT_CAPACITY;
}
// Init constructor
Vector(int Capacity) : size(0), capacity(Capacity) {
Data = new T[capacity];
}
// Destructor
~Vector() {
size=0;
Data = NULL;
delete[] Data;
}
// Accessors
int GetSize() const {return size;}
T* GetData() {return Data;}
void SetSize(const int size) {this->size = size;}
// Functions
void Add(const T& newElement) {
Insert(newElement, size);
}
void Insert(const T& newElement, int index) {
// Check if index is in bounds
if((index<0) || (index>capacity)) {
std::stringstream err;
err << "Vector::Insert(): Index " << index << " out of bounds (0-" << capacity-1 << ")";
throw err.str();
}
// Check capacity
if(size>=capacity)
Grow();
// Move all elements right of index to the right
for(int i=size-1; i>=index; i--)
Data[i+1]=Data[i];
// Put the new element at the specified index
Data[index] = newElement;
size++;
}
void Remove(int index) {
// Check if index is in bounds
if((index<0) || (index>capacity-1)) {
std::stringstream err;
err << "Vector::Remove():Index " << index << " out of bounds (0-" << capacity-1 << ")";
throw err.str();
}
// Move all elements right of index to the left
for(int i=index+1; i<size; i++)
Data[i-1]=Data[i];
}
// Index operator
T& operator [] (int index) const {
// Check if index is in bounds
if((index<0) || (index>capacity-1)) {
std::stringstream err;
err << "Vector operator[]:Index " << index << " out of bounds (0-" << capacity-1 << ")";
throw err.str();
}
return Data[index];
}
// Assignment oper
Vector<T>& operator = (const Vector<T>& right) {
Data = new T[right.GetSize()];
for(int i=0; i<right.GetSize(); i++)
Data[i] = right[i];
size = right.GetSize();
return *this;
}
private:
T *Data;
int size; // Current vector size
int capacity; // Max size of vector
void Grow() {
capacity+=CAPACITY_BOOST;
T* newData = new T[capacity];
for(int i=0; i<capacity; i++)
newData[i] = Data[i];
// Dispose old array
Data = NULL;
delete[] Data;
// Assign new array to the old array's variable
Data = newData;
}
};
The input file:
aero
asia
biz
cat
com
coop
edu
gov
info
int
jobs
mil
mobi
museum
name
net
org <-- crashes when this line is read
pro
tel
travel
The error Visual Studio throws is:
Unhandled exception at 0x5fb04013 (msvcp100d.dll) in Email4.exe: 0xC0000005: Access violation reading location 0xabababbb.
The problem is in your grow function:
void Grow() {
capacity+=CAPACITY_BOOST;
T* newData = new T[capacity];
for(int i=0; i<capacity; i++)
newData[i] = Data[i];
You increase the capacity, but then copy elements that didn't exist in the old array. It should be something like:
void Grow() {
int old_capacity = capacity;
capacity+=CAPACITY_BOOST;
T* newData = new T[capacity];
for(int i=0; i<old_capacity; i++)
newData[i] = Data[i];
You also NULL out Data before deleting it in both Grow and the destructor, which causes a memory leak. In both cases, you really don't need to set it to NULL at all, since there's no change of it being accidentally double-deleted (in Grow it's set to a new pointer immediately, in the destructor the object's lifetime is over). So just
delete[] Data;
alone is fine.
Also I think
if(size>=capacity)
can be:
if(size == capacity)
since size should never be over capacity. That would mean you'd already overflowed the buffer.
Matthew is probably right. Still, there's a valuable lesson to be learned here.
When you hit a problem like this, don't stop walking your code in your ReadTlds function. Keep walking inside the Vector class. Functions like Insert and Grow probably hold the error, but if you don't walk through them, you'll never find it.
Debugging is it's own very special skill. It takes a long time to get it down pat.
edit it's a late night and I misread your code, but I left my post to comment back
Also in the default ctor you do
Data = NULL;
capacity=INIT_CAPACITY;
(EDIT: expanded explanation here)
But never allocate the memory for Data. Shouldn't it be:
Vector() {
Data= new T[INIT_CAPCITY];
size=0;
capacity=INIT_CAPACITY;
}
And remove is missing
--size
EDIT:
Fellow readers help me out here:
Data is of type T* but everywhere else you are assigning and allocating it just like T instead of T* . My C++ days are too long gone to remember whether using a T& actually resolves this.
Also I can't remember that if you have an array of pointers and destruct it, that the dtor for the single instances in the array are destroyed.
Also in the assignment operator, wouldn't you be copying the pinters? so you just have to rely on the fact the the instance where you copyid from is never deleted (because then your objects would be dead too).
hth Mario