I'm writing for class an implementation of Conway's Game of Life in a toroid. The function cargarToroide (loadToroid) should load from a file into the vector the appropriate status (alive or dead - True or False - 1 or 0) of each cell (celda), it's signature is as follows:
toroide cargarToroide(string nombreArchivo, bool &status);
nombreArchivo is the name of the file, and status should be false if there is any problem loading the file or in the format of the file.
The data structure is defined like this (I cannot change it):
typedef vector< vector<bool> > toroide;
the file is structured like this:
numberOfLines numberOfColums
list of the values of the cells
number of live cells
For example:
4 4
1 0 0 0
0 0 1 0
0 0 0 1
0 1 0 0
4
The thing is, I cannot find a way to make it work. I've read online that vector<bool> has problems when you try to load it the usual way, which was the first thing I've tried.
toroide cargarToroide(string nombreArchivo, bool &status)
{
toroide t;
ifstream fi (nombreArchivo);
int cantidadFilas, cantidadColumnas;
int celda;
if(!fi){
status = false;
}
fi >> cantidadFilas;
fi >> cantidadColumnas;
for(int i = 0; i < cantidadFilas; i++) {
for (int j = 0; j < cantidadColumnas; j++) {
fi >> celda;
if(celda == 1) {
t[i].push_back(true);
}
else if(celda == 0){
t[i].push_back(false);
}
else{
status = false;
return t;
}
}
}
return t;
}
I've also tried defining celda as a boolean and just using
t[i].push_back(celda);
What would be the best way to approach this using C++11?
You need to resize the outer vector before you can use operator[] on it. You should also use the proper type (bool) when reading the data and check the input file for errors. I've commented in the code:
#include <iostream>
#include <fstream>
#include <vector>
typedef std::vector< std::vector<bool> > toroide;
// Both cargarToroide and cargarToroide_improved can be used
bool cargarToroide_improved(const std::string& nombreArchivo, toroide& in_toroide)
{
std::ifstream fi(nombreArchivo);
if(!fi) return false;
int cantidadFilas, cantidadColumnas, liveCells=0;
// use a bool to read the bool data
bool celda;
fi >> cantidadFilas;
fi >> cantidadColumnas;
// check if the stream is in a failed state
if(fi.fail()) return false;
// Temporary used to not mess with in_toroide until we're finished.
// Create it with cantidadFilas default inserted rows
toroide t(cantidadFilas);
for(auto& row : t) {
// default insert columns into the row
row.resize(cantidadColumnas);
for (int col = 0; col < cantidadColumnas; ++col) {
fi >> celda;
// check if the stream is in a failed state
// (non-bool read or the file reached eof())
if(fi.fail()) return false;
// set column value in the row
row[col] = celda;
// count live cells
liveCells += celda;
}
}
// compare live cells in matrix with checksum
int cmpLive;
fi >> cmpLive;
if(fi.fail() || cmpLive!=liveCells) return false;
// a successful toroide was read, swap your temporary
// toroide with the user supplied one
std::swap(t, in_toroide);
return true;
}
// if the signature of this function really can't be changed (which it should),
// make it a proxy for the function with a slightly nicer interface
// Like this:
toroide cargarToroide(std::string nombreArchivo, bool &status)
{
toroide rv;
status = cargarToroide_improved(nombreArchivo, rv);
return rv;
}
Using the improved signature:
int main(int argc, char* argv[]) {
std::vector<std::string> args(argv+1, argv+argc);
for(auto& file : args) {
toroide my_toroide;
if(cargarToroide_improved(file, my_toroide)) {
for(auto& r : my_toroide) {
for(auto c : r) {
std::cout << c << " ";
}
std::cout << "\n";
}
} else {
std::clog << "failed loading " << file << "\n";
}
}
}
Using the signature you're forced to use:
int main(int argc, char* argv[]) {
std::vector<std::string> args(argv+1, argv+argc);
for(auto& file : args) {
bool status;
toroide my_toroide = cargarToroide(file, status);
if(status) {
for(auto& r : my_toroide) {
for(auto c : r) {
std::cout << c << " ";
}
std::cout << "\n";
}
} else {
std::clog << "failed loading " << file << "\n";
}
}
}
If you know the number of rows at compile time (as you do in this case) you can use resize. In this case you will have to do the below before both the for loops.
t.resize(cantidadFilas);
In fact, you can do the same for the columns as well. Then you would no longer need to use push_back in the inner for loop as well.
If do not know the number of rows then you will just use push_back and add the rows to toroide. Then you would add the below lines before the second for loop.
vector<bool> row;
t.push_back(row)
Related
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 :
I'm new to C++ and I'm trying to return a struct from a vector of structs by using 2 search criteria.
The function find_city is returning me everything from the defined range, regardless of whether it exists inside the vector of struct.
Here's my code:
struct cityLoc
{
int hRange;
int vRange;
int cityCode;
string cityName;
};
vector<cityLoc> cl1;
// the vector has already been preloaded with data
// function to return my struct from the vector
cityLoc find_city(int hRange, int vRange)
{
for (size_t i = 0; i < cl1.size(); i++)
{
if ((cl1[i].hRange = hRange) && (cl1[i].vRange = vRange))
{
return cl1[i];
}
}
}
int main()
{
for (int i = 0; i < 8; i++)
{
for (int j = 0; j <= 8; j++)
{
cityLoc this_city;
this_city = find_city(i, j);
cout << this_city.hRange << ", " << this_city.vRange << endl;
}
}
return 0;
}
Also, aside from this question, I was previously looking into std::find_if and didn't understand it. If I have the following code, what is the output? How do I modify it such that it returns a struct?
auto it = find_if(cl1.begin(), cl1.end(), [](cityLoc& cl) { return cl.hRange == 1; } );
You have a bug here:
if ((cl1[i].hRange = hRange) && (cl1[i].vRange = vRange))
Those = are assignments, not comparisons! Please enable compiler warnings and you won't be hurt by such obvious typos in future.
std::find_if will return the iterator to the found struct entry if it is successful, std::vector::end() otherwise. So, you should first validate the returning iterator if it is valid or not.
For example:
auto it = std::find_if( cl1.begin(), cl1.end(),
[](const cityLoc& cl) { return cl.hRange == 1; } );
if ( it == cl1.end() )
{
// ERROR: Not found! Return error code etc.
return -1;
}
// And, if found, process it here...
std::cout << it->hRange << '\n';
std::cout << it->vRange << '\n';
The criteria (predicate) part in std::find_if is a lambda expression.
I need some help for a concurrent c++ programming.
I have a file of names, named "names.txt", in this format:
0 James
1 Sara
2 Isaac
And I have another file named "op.txt" that contains some operations on names file, in this format:
0 1 + // this means add Sara to James and store it in 0 position
1 2 $ // this means swap values in position 1 and position 2
and a file "output.txt" that has the output of operations, in this format:
0 JamesSara
1 Isaac
2 Sara
The problem says that create a thread for read names.txt and op.txt and store them. Next create some variable threads to do operations concurrently and at last do the output.txt in a thread.
Here is my code for this problem, and it works correctly when number of concurrent threads are
greater then 2. But the output for 1 and 2 thread are incorrect.
What I missed in this code?
#include <fstream>
#include <iostream>
#include <vector>
#include <sstream>
#include <cstdlib>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <deque>
using namespace std;
std::mutex _opMutex;
std::condition_variable _initCondition;
std::condition_variable _operationCondition;
int _counter = 0;
int _initCounter = 0;
int _doOperationCounter = 0;
struct OperationStruct
{
int firstOperand;
int secondOperand;
char cOperator;
};
const int THREADS = 5;
std::deque<std::pair<int, string> > _nameVector;
std::deque<OperationStruct> _opStructVec;
void initNamesAndOperations()
{
ifstream infile;
std::pair<int, string> namePair;
infile.open("names.txt");
if (!infile)
{
cout << "Unable to open file";
exit(-1);
}
int id;
string value;
while (infile >> id >> value)
{
namePair.first = id;
namePair.second = value;
_nameVector.push_back(namePair);
}
infile.close();
infile.open("op.txt");
if (!infile)
{
cout << "Unable to open file";
exit(-1);
}
int firstOperand;
int secondOperand;
char cOperator;
while (infile >> firstOperand >> secondOperand >> cOperator)
{
OperationStruct opSt;
opSt.firstOperand = firstOperand;
opSt.secondOperand = secondOperand;
opSt.cOperator = cOperator;
_opStructVec.push_back(opSt);
++_initCounter;
}
infile.close();
return;
}
void doOperationMath(int firstIndex, string firstValue, string secondValue, char cOp)
{
//basic mathematics
switch (cOp)
{
case '+':
{
for (int i = 0; i < _nameVector.size(); ++i)
{
std::pair<int, string> acc = _nameVector[i];
if (acc.first == firstIndex)
{
acc.second = firstValue + secondValue;
_nameVector[i].second = acc.second;
}
}
}
break;
default:
break;
}
++_doOperationCounter;
}
void doOperationSwap(int firstIndex, int secondIndex, string firstValue, string secondValue)
{
//swap
for (int i = 0; i < _nameVector.size(); ++i)
{
if (_nameVector[i].first == firstIndex)
_nameVector[i].second = secondValue;
if (_nameVector[i].first == secondIndex)
_nameVector[i].second = firstValue;
}
++_doOperationCounter;
}
void doOperations()
{
while (_doOperationCounter < _initCounter)
{
std::unique_lock<mutex> locker(_opMutex);
_initCondition.wait(locker, [](){return !_opStructVec.empty(); });
OperationStruct opSt = _opStructVec.front();
_opStructVec.pop_front();
locker.unlock();
_operationCondition.notify_one();
int firstId = opSt.firstOperand;
int secondId = opSt.secondOperand;
char cOp = opSt.cOperator;
string firstValue = "";
string secondValue = "";
for (int j = 0; j < _nameVector.size(); ++j)
{
std::pair<int, string> acc = _nameVector[j];
if (firstId == acc.first)
firstValue = acc.second;
if (secondId == acc.first)
secondValue = acc.second;
}
if (cOp == '$')
{
doOperationSwap(firstId, secondId, firstValue, secondValue);
}
else
{
doOperationMath(firstId, firstValue, secondValue, cOp);
}
}
return;
}
void doOutputFile()
{
ofstream outfile;
outfile.open("sampleOutput.txt", std::ios::out | std::ios::app);
if (!outfile)
{
cout << "Unable to open the file";
exit(-1);
}
while (_counter < _initCounter)
{
std::unique_lock<mutex> locker(_opMutex);
_operationCondition.wait(locker, [](){return !_nameVector.empty(); });
auto accPair = _nameVector.front();
_nameVector.pop_front();
locker.unlock();
outfile << accPair.first << " " << accPair.second << endl;
++_counter;
}
return;
}
int main()
{
thread th1(initNamesAndOperations);
std::vector<thread> operationalThreads;
for (int i = 0; i < THREADS; ++i)
{
operationalThreads.push_back(thread(doOperations));
}
thread th3(doOutputFile);
th1.join();
for (auto& opthread : operationalThreads)
opthread.join();
th3.join();
return 0;
}
If a variable is modified from multiple threads, you might have to use some synchronisation to ensure that the proper value is read. The simplest way would probably be to use std::atomic for your variables to ensure that operations are properly sequenced.
Also, there is nothing in your code to ensure that your doOperations thread won't finish before you have read the whole file.
Obviously, you need to either read the whole data first or have a way to wait for some data to become available (or to reach the end of data). If reading the initial data is fast but processing is slow, then the easier solution is to read the data before starting processing threads.
What is probably happening is that if you create a lot of threads, by the time you create the last thread, the initNamesAndOperations would have read the whole file.
I highly recommend you to buy and read C++ Concurrency in Action by Anthony Williams. By reading such book, you will get a good understanding on modern C++ multithreading and it will help you a lot to write correct code.
I was assigned to create an array check (to see if the array is increasing, decreasing, or neither [then exiting if neither]) and a recursive binary search for one of my assignments. I was able to do these things after some help from my peers, but I need help in finding what seems to be causing the error
terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_S_construct null not valid
Aborted
when running the code. I Googled this error and this error seems to be vague or I just am not understanding. It compiles without errors, but I need help in what finding what I did wrong. It is able to run without the binarySearchR function and its associating code, as the array check on its own was the previous assignment. Below is the code, and I thank you so much in advance!
#include <iosteam>
#include <string>
#include <cstdlib>
#include <fstream>
using namespace std;
int checkArraySort (string *fileLines, int numberOfLines);
int binarySearchR (string *fileLines, string searchKey, int iMin, int iMax);
int main ()
{
int numberOfLines = 0;
string searchKey = 0;
cout << "Input search key: ";
cin >> searchKey;
ifstream fileIn;
fileIn.open("words_in.txt");
string line;
if (fileIn.eof()) /* Checks file to see if it is blank before proceeding */
{
exit (EXIT_SUCCESS);
}
else
{
while(!(fileIn.eof()))
{
fileIn >> line;
numberOfLines++;
}
fileIn.close(); /* closes fileIn, need to reopen to reset the line location */
fileIn.open("words_in.txt");
string *fileInLines;
fileInLines = new string[numberOfLines];
for (int i = 0; i < numberOfLines; i++)
{
fileIn >> line;
fileInLines[i] = line;
}
fileIn.close(); /* closes fileIn */
int resultingCheck = checkArraySort(fileInLines, numberOfLines);
if (resultingCheck == -1)
{
cout << "The array is sorted in descending order." << endl;
}
else if (resultingCheck == 1)
{
cout << "The array is sorted in ascending order." << endl;
}
else
{
cerr << "ERROR: Array not sorted!" << endl;
exit (EXIT_FAILURE);
}
int searchResult = binarySearchR (fileInLines, searchKey, 0, numberOfLines);
if (!searchResult == -1)
{
cout << "Key found at index " << searchResult << "." << endl;
}
else
{
cout << "Key not found at any index." << endl;
}
exit (EXIT_SUCCESS);
}
}
int checkArraySort (string *fileLines, int numberOfLines)
{
int result = 1; /* Ascending by default */
for (int i = 1; i < numberOfLines; i++) /* Checks if decending */
{
if (fileLines[i] < fileLines[i-1])
{
result = -1;
}
}
if (result == -1) /* Makes sure it is descending (or if it is neither) */
{
for (int i = 1; i < numberOfLines; i++)
{
if (fileLines[i] > fileLines[i-1])
{
result = 0;
}
}
}
return result;
}
int binarySearchR (string *fileLines, string searchKey, int iMin, int iMax)
{
// so, its gotta look at the center value and each times, it discards half of the remaining list.
if (iMax < iMin) /* If the minimum is greater than the maximum */
{
return -1;
}
else
{
int iMid = (iMin + iMax) / 2;
if (fileLines[iMid] > searchKey) /* If the key is in the lower subset */
{
return binarySearchR (fileLines, searchKey, iMin, iMid - 1);
}
else if (fileLines[iMid] < searchKey) /*If the key is in the upper subset */
{
return binarySearchR (fileLines, searchKey, iMin, iMid + 1);
}
else /*If anything else besides the two */
{
return iMid;
}
}
}
The easy way: add a bunch of cout s to see where you program goes and what the values are.
Pros
Easy to do
Cons
Requires a recompile each time you want to add more info
The hard way: Learn to use a debugger
Pros
Can inspect "on the fly"
Don't need to rebuild
Can use what you learn in every other C++ program
Cons
Requires a bit of research to learn how to do it.
I'm currently building a game and in this part my goal is to update a previously created file of High Scores ("rank.txt")
To do so, I've created a couple of functions so they can read what's in the txt file and so it can update it. Though the reading is ok, while adding the update function it gives me an error and tells me abort() has been called.
void highScoresUpdate(vector<PlayerInfo> player)
{
// This function is responsible for updating the High Scores Table present in the RANK.TXT file
vector<HighScoresStruct> highScores_temp;
HighScoresStruct temp;
ofstream out_file_3("rank.txt");
highScores_temp = readHighScores("rank.txt");
for (int k = 0; k < player.size(); k++)
{
player[k].name = temp.name;
player[k].score = temp.score;
player[k].time = temp.time;
highScores_temp.push_back(temp);
}
sort(highScores_temp.begin(), highScores_temp.end(), compareByScore);
for (int i = 0; i < highScores_temp.size(); i++)
{
out_file_3 << highScores_temp[i].name << endl << highScores_temp[i].score << endl << highScores_temp[i].time << endl;
}
out_file_3.close();
}
For background information, so i don't spam this thread with code all you need to know is that readHighScores is the function responsible for extracting information from the txt file and placing it on a vector .
The structs and comparebyScore funtion are listed below.
bool compareByScore(const HighScoresStruct &a, const HighScoresStruct &b)
{
if (a.score < b.score)
return true;
else if (a.score == b.score)
{
if (a.time > b.time)
return true;
else
return false;
}
else
return false;
}
struct PlayerInfo
{
string name;
unsigned int score;
vector<char> hand;
bool inGame;
unsigned int time;
};
struct HighScoresStruct
{
string name;
unsigned int score;
unsigned int time;
};
So... Can anyone help me?