I'm trying to count the number of specific characters from a file. The problem I have run into is that the output is a huge number that does not match up with the amount of each letter in the file.
RainOrShine.txt
RRCSSSCSCRRRCSSSCSSRSCCRCRRCSS
SSSCCSSSCCSSSCCSSSCRCRCCSSSSSS
SSSSCSSSCSSSCRRCCCSSSSSCSSSSCS
#include <iostream>
#include <string>
#include <fstream>
#include <cstring>
using namespace std;
int main()
{
string filelocation = "C:/Users/erizj/OneDrive/Documents/RainOrShine.txt";
ifstream textfile;
textfile.open(filelocation.c_str());
char weather[3][30];
int countR,countC,countS = 0;
if (!textfile)
{
cout << "Error opening the file.";
return 0;
}
else
{ // Read weather data in from file
for (int row = 0; row < 3; row++)
{
for (int col = 0; col < 30; col++)
{
textfile >> weather[row][col];
if(weather[row][col]=='R'){
countR++;
}
if(weather[row][col]=='C'){
countC++;
}
if(weather[row][col]=='S'){
countS++;
}
}
}
}
cout<<"Rainy Days during 3-Month Period: "<<countR<<endl;
cout<<"Cloudy Days during 3-Month Period: "<<countC<<endl;
cout<<"Sunny Days during 3-Month Period: "<<countS<<endl;
//cout<<"Rainy Days in June: "<<
textfile.close();
return 0;
}
Output:
Rainy Days during 3-Month Period: 4201688
Cloudy Days during 3-Month Period: 6356911
Sunny Days during 3-Month Period: 50
Does it have something to do with the counter that I set up? Thank in advance.
int countR,countC,countS = 0;
initializes countS but leaves countR and countC uninitialized.
Your program is assuming that there are a fixed number of rows and columns in the data. This could be a problem.
I suggest a more flexible approach, no assumptions about how the data is organized or the quantity of data.
Let's define a structure for our database. The data can be stored sequentially, but we need the data structure to be dynamic: std::vector.
Now to read in the data before making any analysis:
std::vector<char> database;
std::string text_line; // Easier to read in a lot of data
while (getline(data_file, text_line))
{
std::string::size_type index;
const std::string::size_type length = text_line.length();
for (index = 0; index < length; ++index)
{
const char c = text_line[index];
if ((c == 'R') || (c == 'S') || (c == 'C'))
{
database.push_back(c);
}
}
}
Since the data is read into a database, you can analyze it:
unsigned int duration = 0;
unsigned int rain_quantity = 0;
unsigned int sunny_quantity = 0;
unsigned int cloudy_quantity = 0;
// Set up the duration for the first 30 days
duration = 30;
if (database.size() < 30)
{
duration = database.size();
}
for (unsigned int index = 0; index < duration; ++index)
{
const char c = database[index];
if (c == 'C')
{
++cloudy_quantity;
}
else
{
if (c == 'R')
{
++rain_quantity;
}
else
{
++sunny_quantity;
}
}
}
You can perform other analysis without reading the data from the file.
You need to initialize all of your integer variables separately.
int countR = 0, countS = 0, countT = 0;
Related
So I'm pretty new to c++, and this is the second program I've ever written in it. The first program was a complete mess but it worked, this program I decided to just put all in one file. My problem is, it just runs incredibly slowly. I'm pretty sure it's because of just how many characters it has to iterate through, but the requirements are that it can crack a password of a specific length with any special characters like these. Other than the number of characters it has to go through I'm not sure what, if anything, is making it run so slowly. I'm wondering if it's something I've done that is improper and making it take so long. Is this just about how fast a password cracker like this can run?
// Password Cracker.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <string>
#include <thread>
#include <atomic>
#include <vector>
#include <cmath>
#include <sstream>
std::string convertToString(char*, int);
void passwordBruteForcer(std::string, long long, long long);
std::atomic<bool> isPasswordFound = false;
std::string foundString;
int main()
{
// Given some count of the amount of symbols that can exist in the password
// We will calulcate how many possible passwords there are then divide work evenly between threads
// In this case the possible symbols are all ascii codes from 32 to 126
int numOfSymbols = 95;
// Declarations;
int numberOfThreads = 8;
std::string password;
std::vector<std::thread> vecOfThreads;
// Loop waits for correct user input (problem required that password length be 5, change this if you want)
while (true)
{
// Prompting user input
std::cout << "Please enter a password of length 5 \n";
std::cout << "password: ";
std::getline(std::cin, password);
if (password.length() == 5)
{
break;
}
else
{
std::cout << "Invalid password \n";
}
}
long long amountOfPossiblePasswords = pow(numOfSymbols, password.length());
// Creates threads running brute force cracker
for (int threadNumber = 1; threadNumber <= numberOfThreads; threadNumber++)
{
long long startingNumber = (amountOfPossiblePasswords * (threadNumber - 1)) / numberOfThreads;
long long endNumber = (amountOfPossiblePasswords * threadNumber) / numberOfThreads;
long long remainder = amountOfPossiblePasswords % numberOfThreads;
if (threadNumber == numberOfThreads)
{
vecOfThreads.push_back(std::thread(passwordBruteForcer, password, startingNumber, endNumber + remainder));
}
else
{
vecOfThreads.push_back(std::thread(passwordBruteForcer, password, startingNumber, endNumber));
}
}
// Wait to join thread
for (std::thread & th : vecOfThreads)
{
if (th.joinable())
th.join();
}
// Prints the password the algorithm found
if (isPasswordFound)
{
std::cout << foundString;
}
return 0;
}
// Will go through all "printable" ASCII characters
void passwordBruteForcer(std::string passStr, long long startingNumber, long long endNumber)
{
int firstChar = 32;
int lastChar = 126;
int length = passStr.length();
int numOfChar = 95;
int numOfTries = pow(numOfChar, length);
char* guess = new char[length];
// Converts decimal to base num of chars then puts that char into array
// For ex. 0 will be first char, and a 1 will be the next char, the number that represents the amount of possible passwords
// will set this to be all lastChar
long long numToBeConvert = startingNumber;
for (int i = (length - 1); i >= 0; i--)
{
guess[i] = firstChar + (numToBeConvert % numOfChar);
numToBeConvert /= numOfChar;
}
// This creates a string based on initialized guess then tests it
std::string comparisonPasswordString = convertToString(guess, length);
if (comparisonPasswordString == passStr)
{
isPasswordFound = true;
foundString = comparisonPasswordString;
delete[] guess;
return;
}
// This loop goes from startingNumber to endNumber testing all passwords
for(long long i = startingNumber; i < endNumber; i++)
{
if (isPasswordFound == true)
{
break;
}
long long numForGuess = i;
for (int j = (length - 1); j >= 0; j--)
{
guess[j] = firstChar + (numForGuess % numOfChar);
numForGuess /= numOfChar;
}
comparisonPasswordString = convertToString(guess, length);
if (comparisonPasswordString == passStr)
{
isPasswordFound = true;
foundString = comparisonPasswordString;
break;
}
}
delete[] guess;
}
std::string convertToString(char* charArr, int length)
{
std::string convString;
for (int i = 0; i < length; i++)
{
convString.push_back(charArr[i]);
}
return convString;
}
I know this question is a duplicate one, but I couldn't find any other topic similar to my code.
The problem statement is as followed:
There is a CSV file with 16,000 lines. A serial version of the program is extracting those rows with a price (SalePrice is a column head in the CSV) higher than a specific value (threshold) given to the program with command-line arguments and calculating their Mean and Standard Derivation which will be used for further computations.
This larger CSV file is broken into 4 CSV files for the parallel version. Each thread is assigned to one CSV file and should do the same calculations (Calculating Mean and STD of rows with price higher than a specific value named threshold in my code).
Since the data is large enough, I don't think this is because of the multithreading overhead.
I would be thankful if someone could please help me find out what part is slowing down my parallel version?
#include <iostream>
#include <fstream>
#include <vector>
#include <math.h>
#include <iomanip>
#include <pthread.h>
#include <stdio.h>
#include <time.h>
#include <sys/stat.h>
#include <unistd.h>
using namespace std;
#define COMMA ','
#define EMPTY_STR ""
#define FILENAME "dataset.csv"
#define CLASSIFIER "GrLivArea"
#define SALE_PRICE "SalePrice"
const int MAX_THREAD_NUMBERS = 20;
int NUMBER_OF_THREADS;
int threshold;
int expensive_cnt[MAX_THREAD_NUMBERS];
vector<string> lines;
string head;
double _std;
long sum[MAX_THREAD_NUMBERS];
long ps[MAX_THREAD_NUMBERS];
long sumsq[MAX_THREAD_NUMBERS];
double mean;
int total_items;
int total_expensive_cnt;
struct Item
{
int x;
bool category;
};
vector<Item> items[MAX_THREAD_NUMBERS];
int getColNum(const string& head, const string& key)
{
int cnt = 0;
string cur = EMPTY_STR;
for (int i = 0 ; i < head.size() ; i++)
{
if (head[i] == COMMA)
{
if (cur == key)
return cnt;
cnt++;
cur = EMPTY_STR;
}
else
cur += head[i];
}
if (cur == key)
return cnt;
return -1;
}
vector<int> separateByComma(string s)
{
vector<int> res;
string cur = EMPTY_STR;
for (int i = 0 ; i < s.size() ; i++)
if (s[i] == COMMA)
{
res.push_back(stoi(cur));
cur = EMPTY_STR;
}
else
cur += s[i];
res.push_back(stoi(cur));
return res;
}
void* calcSums(void* tid)
{
long thread_id = (long)tid;
string filename = "dataset_" + to_string(thread_id) + ".csv";
ifstream fin(filename);
string head;
fin >> head;
int classifierColNum = getColNum(head, CLASSIFIER);
if (classifierColNum == -1)
{
printf("NO GrLivArea FOUND IN HEAD OF CSV\n");
exit(-1);
}
int priceColNum = getColNum(head, SALE_PRICE);
if (priceColNum == -1)
{
printf("NO SalePrice FOUND IN HEAD OF CSV\n");
exit(-1);
}
string line;
while (fin >> line)
{
vector<int> cur = separateByComma(line);
bool category = (cur[priceColNum] >= threshold);
Item item{cur[classifierColNum], category};
if (category)
{
sum[thread_id] += item.x;
sumsq[thread_id] += (item.x * item.x);
expensive_cnt[thread_id]++;
}
items[thread_id].push_back(item);
}
fin.close();
pthread_exit(NULL);
}
void calcMeanSTD()
{
string line;
for (int i = 0 ; ; i++)
{
struct stat buffer;
string name = "dataset_" + to_string(i) + ".csv";
if (!(stat (name.c_str(), &buffer) == 0))
break;
NUMBER_OF_THREADS++;
}
pthread_t threads[NUMBER_OF_THREADS];
int return_code;
for (long tid = 0 ; tid < NUMBER_OF_THREADS ; tid++)
{
return_code = pthread_create(&threads[tid], NULL, calcSums, (void*)tid);
if (return_code)
{
printf("ERROR; return code from pthread_create() is %d\n", return_code);
exit(-1);
}
}
for (long tid = 0 ; tid < NUMBER_OF_THREADS ; tid++)
{
return_code = pthread_join(threads[tid], NULL);
if (return_code)
{
printf("ERROR; return code from pthread_join() is %d\n", return_code);
exit(-1);
}
}
double total_sum = 0;
double total_sum_sq = 0;
total_expensive_cnt = 0;
total_items = 0;
for (int i = 0 ; i < NUMBER_OF_THREADS ; i++)
{
total_sum += sum[i];
total_sum_sq += sumsq[i];
total_expensive_cnt += expensive_cnt[i];
total_items += items[i].size();
}
mean = total_sum / total_expensive_cnt;
_std = sqrt((total_sum_sq - ((total_sum * total_sum) / (total_expensive_cnt))) / (total_expensive_cnt));
}
int main(int argc, char *argv[])
{
threshold = atoi(argv[1]);
calcMeanSTD();
cout << mean << " " << _std << endl;
return 0;
}
Please let me know if any part is not understandable.
Here are some run-time values:
Read CSV (Serial): 0.043268s Calculations (Serial): 0.000151s
The exact time calculation isn't much easy in the multithreaded version here since the calculations and file reading are done in the same while loop which is not separable here. There also many thread switches. Anyway, their sum is about: 0.14587s
As it can be seen, the amount of time needed to read from files is almost 300 times as doing the math calculations.
Thanks to the answers in the comment, I found out what is happening:
I tried increasing the number of rows in my CSV files to see if the parallelization is working.
The run-time values for a CSV file with 1000000 rows are:
Parallel: real 0m0.558s user 0m2.173s sys 0m0.020s
Serial: real 0m1.834s user 0m1.818s sys 0m0.016s
Since I am using 4 threads, I expect 1.834 divided by 0.558 to be near to 4 which actually is 3.28 and is fair enough.
This run-time values for smaller CSV files aren't showing these results which seems to be because of the simple math computations in my code.
The bottleneck of this code is the section where I am reading from CSV files. This section seems to be serial since it is reading from a disk.
There is also a problem of False Sharing in this code
which causes cache contention due to updates of different memory locations by different threads when these locations share the same cache line mapping. There are many solutions to this problem, for example, I can introduce padding into these arrays to make sure that elements accessed by multiple threads do not share cache lines. Or, more simply, work with thread-local variables instead of arrays, and, in the end, update the array elements only once.
*Sorry about my poor English. If there is anything that you don't understand, please tell me so that I can give you more information that 'make sence'.
**This is first time asking question in Stackoverflow. I've searched some rules for asking questions correctly here, but there should be something I missed. I welcome all feedback.
I'm currently solving algorithm problems to improve my skill, and I'm struggling with one question for three days. This question is from https://algospot.com/judge/problem/read/RESTORE , but since this page is in KOREAN, I tried to translate it in English.
Question
If there are 'k' pieces of partial strings given, calculate shortest string that includes all partial strings.
All strings consist only lowercase alphabets.
If there are more than 1 result strings that satisfy all conditions with same length, choose any string.
Input
In the first line of input, number of test case 'C'(C<=50) is given.
For each test case, number of partial string 'k'(1<=k<=15) is given in the first line, and in next k lines partial strings are given.
Length of partial string is between 1 to 40.
Output
For each testcase, print shortest string that includes all partial strings.
Sample Input
3
3
geo
oji
jing
2
world
hello
3
abrac
cadabra
dabr
Sample Output
geojing
helloworld
cadabrac
And here is my code. My code seems to work perfect with Sample Inputs, and when I made test inputs for my own and tested, everything worked fine. But when I submit this code, they say my code is 'wrong'.
Please tell me what is wrong with my code. You don't need to tell me whole fixed code, I just need sample inputs that causes error with my code. Added code description to make my code easier to understand.
Code Description
Saved all input partial strings in vector 'stringParts'.
Saved current shortest string result in global variable 'answer'.
Used 'cache' array for memoization - to skip repeated function call.
Algorithm I designed to solve this problem is divided into two function -
restore() & eraseOverlapped().
restore() function calculates shortest string that includes all partial strings in 'stringParts'.
Result of resotre() is saved in 'answer'.
For restore(), there are three parameters - 'curString', 'selected' and 'last'.
'curString' stands for currently selected and overlapped string result.
'selected' stands for currently selected elements of 'stringParts'. Used bitmask to make my algorithm concise.
'last' stands for last selected element of 'stringParts' for making 'curString'.
eraseOverlapped() function does preprocessing - it deletes elements of 'stringParts' that can be completly included to other elements before executing restore().
#include <algorithm>
#include <iostream>
#include <vector>
#include <cstring>
#include <string>
#define MAX 15
using namespace std;
int k;
string answer; // save shortest result string
vector<string> stringParts;
bool cache[MAX + 1][(1 << MAX) + 1]; //[last selected string][set of selected strings in Bitmask]
void restore(string curString, int selected=0, int last=0) {
//base case 1
if (selected == (1 << k) - 1) {
if (answer.empty() || curString.length() < answer.length())
answer = curString;
return;
}
//base case 2 - memoization
bool& ret = cache[last][selected];
if (ret != false) return;
for (int next = 0; next < k; next++) {
string checkStr = stringParts[next];
if (selected & (1 << next)) continue;
if (curString.empty())
restore(checkStr, selected + (1 << next), next + 1);
else {
int check = false;
//count max overlapping area of two strings and overlap two strings.
for (int i = (checkStr.length() > curString.length() ? curString.length() : checkStr.length())
; i > 0; i--) {
if (curString.substr(curString.size()-i, i) == checkStr.substr(0, i)) {
restore(curString + checkStr.substr(i, checkStr.length()-i), selected + (1 << next), next + 1);
check = true;
break;
}
}
if (!check) { // if there aren't any overlapping area
restore(curString + checkStr, selected + (1 << next), next + 1);
}
}
}
ret = true;
}
//check if there are strings that can be completely included by other strings, and delete that string.
void eraseOverlapped() {
//arranging string vector in ascending order of string length
int vectorLen = stringParts.size();
for (int i = 0; i < vectorLen - 1; i++) {
for (int j = i + 1; j < vectorLen; j++) {
if (stringParts[i].length() < stringParts[j].length()) {
string temp = stringParts[i];
stringParts[i] = stringParts[j];
stringParts[j] = temp;
}
}
}
//deleting included strings
vector<string>::iterator iter;
for (int i = 0; i < vectorLen-1; i++) {
for (int j = i + 1; j < vectorLen; j++) {
if (stringParts[i].find(stringParts[j]) != string::npos) {
iter = stringParts.begin() + j;
stringParts.erase(iter);
j--;
vectorLen--;
}
}
}
}
int main(void) {
int C;
cin >> C; // testcase
for (int testCase = 0; testCase < C; testCase++) {
cin >> k; // number of partial strings
memset(cache, false, sizeof(cache)); // initializing cache to false
string inputStr;
for (int i = 0; i < k; i++) {
cin >> inputStr;
stringParts.push_back(inputStr);
}
eraseOverlapped();
k = stringParts.size();
restore("");
cout << answer << endl;
answer.clear();
stringParts.clear();
}
}
After determining which string-parts can be removed from the list since they are contained in other string-parts, one way to model this problem might be as the "taxicab ripoff problem" problem (or Max TSP), where each potential length reduction by overlap is given a positive weight. Considering that the input size in the question is very small, it seems likely that they expect a near brute-force solution, with possibly some heuristic and backtracking or other form of memoization.
Thanks Everyone who tried to help me solve this problem. I actually solved this problem with few changes on my previous algorithm. These are main changes.
In my previous algorithm I saved result of restore() in global variable 'answer' since restore() didn't return anything, but in new algorithm since restore() returns mid-process answer string I no longer need to use 'answer'.
Used string type cache instead of bool type cache. I found out using bool cache for memoization in this algorithm was useless.
Deleted 'curString' parameter from restore(). Since what we only need during recursive call is one previously selected partial string, 'last' can replace role of 'curString'.
CODE
#include <algorithm>
#include <iostream>
#include <vector>
#include <cstring>
#include <string>
#define MAX 15
using namespace std;
int k;
vector<string> stringParts;
string cache[MAX + 1][(1 << MAX) + 1];
string restore(int selected = 0, int last = -1) {
if (selected == (1 << k) - 1) {
return stringParts[last];
}
if (last == -1) {
string ret = "";
for (int next = 0; next < k; next++) {
string resultStr = restore(selected + (1 << next), next);
if (ret.empty() || ret.length() > resultStr.length())
ret = resultStr;
}
return ret;
}
string& ret = cache[last][selected];
if (!ret.empty()) {
cout << "cache used in [" << last << "][" << selected << "]" << endl;
return ret;
}
string curString = stringParts[last];
for (int next = 0; next < k; next++) {
if (selected & (1 << next)) continue;
string checkStr = restore(selected + (1 << next), next);
int check = false;
string resultStr;
for (int i = (checkStr.length() > curString.length() ? curString.length() : checkStr.length())
; i > 0; i--) {
if (curString.substr(curString.size() - i, i) == checkStr.substr(0, i)) {
resultStr = curString + checkStr.substr(i, checkStr.length() - i);
check = true;
break;
}
}
if (!check)
resultStr = curString + checkStr;
if (ret.empty() || ret.length() > resultStr.length())
ret = resultStr;
}
return ret;
}
void EraseOverlapped() {
int vectorLen = stringParts.size();
for (int i = 0; i < vectorLen - 1; i++) {
for (int j = i + 1; j < vectorLen; j++) {
if (stringParts[i].length() < stringParts[j].length()) {
string temp = stringParts[i];
stringParts[i] = stringParts[j];
stringParts[j] = temp;
}
}
}
vector<string>::iterator iter;
for (int i = 0; i < vectorLen - 1; i++) {
for (int j = i + 1; j < vectorLen; j++) {
if (stringParts[i].find(stringParts[j]) != string::npos) {
iter = stringParts.begin() + j;
stringParts.erase(iter);
j--;
vectorLen--;
}
}
}
}
int main(void) {
int C;
cin >> C;
for (int testCase = 0; testCase < C; testCase++) {
cin >> k;
for (int i = 0; i < MAX + 1; i++) {
for (int j = 0; j < (1 << MAX) + 1; j++)
cache[i][j] = "";
}
string inputStr;
for (int i = 0; i < k; i++) {
cin >> inputStr;
stringParts.push_back(inputStr);
}
EraseOverlapped();
k = stringParts.size();
string resultStr = restore();
cout << resultStr << endl;
stringParts.clear();
}
}
This algorithm is much slower than the 'ideal' algorithm that the book I'm studying suggests, but it was fast enough to pass this question's time limit.
I am having trouble debugging this code so it reads two columns from the file and when the first column (Department is the same it just adds the second column to the old dept already created)This code is having trouble with looping. Any walk through, help would be much appreciated ! Thanks.
#include <iostream>
#include <fstream>
using namespace std;
ifstream inputFile; //stream object
int main()
{
inputFile.open("text.txt");
const int SIZE = 15;
int candy[SIZE];
int dept[SIZE];
int valuecounter = 0;
int Candy;
int Department;
while (inputFile >> Department >> Candy)
{
// Exit loop if we have filled the entire array
if (valuecounter == SIZE)
break;
// Update previous values
for (int index = 0; index < valuecounter; index++)
{
if (dept[index] == Department)
{
candy[index] += Candy;
}
}
// Update current values and increment counter
dept[valuecounter] = Department;
candy[valuecounter] = Candy;
valuecounter++;
}
for (int i = 0; i < valuecounter ; i++)
cout << dept[i] << " " << candy[i] << endl;
inputFile.close();
return 0;
}
and the list of input being for ex:
910 8
450 9
750 10
150 35
750 19
150 18
910 19
390 19
520 6
110 78
300 23
110 1
110 5
120 6
150 16
300 23
110 1
110 5
120 6
150 16
the array should be partially filled. but it produces weird outcome! logic error?
You have initialized valuecounter, and all elements of array dept, to zero. For this reason, in every iteration of the while loop, condition dept[valuecounter] == NULL will always evaluate to true. This means that, in every iteration, only code in the first if statement will execute.
Note that this is not the only problem with this code. As user Crazy Eddie pointed out, using NULL as an integer is considered very bad practice.
EDIT:
Replace your while loop with the following:
while (inputFile >> Department >> Candy)
{
// If this Department already exists ...
for (int index = 0; index < valuecounter; index++)
{
if (dept[index] == Department)
{
// ... update the corresponding value in 'candy' and continue the loop
candy[index] += Candy;
continue;
}
}
// If this Department does not exist, and if there are are more
// available array elements, assign a new Department
if (valuecounter < SIZE)
{
dept[valuecounter] = Department;
candy[valuecounter] = Candy;
valuecounter++;
}
}
int main()
{
const int SIZE = 15;
int candy[SIZE];
int dept[SIZE];
int valuecounter = 0;
int Candy;
int Department;
inputFile.open("text.txt");
if (!inputFile)
{
cout << "\nError opening file" << endl
<< "Exiting\n" << endl;
exit(1);
}
while (valuecounter < SIZE && inputFile >> Department >> Candy)
{
//setting bool to false everytime loop starts over.
bool found = false;
for (int index = 0; index < valuecounter; index++)
{
if (dept[index] == Department)
{
candy[index] += Candy;
//setting bool to true if department found
found = true;
}
}
if (!found)
{
dept[valuecounter] = Department;
candy[valuecounter] = Candy;
valuecounter++;
}
}
inputFile.close();
if (valuecounter == 0)
{
cout << "\n\nEMPTY FILE\nExiting.\n\n" << endl;
exit(1);
}
here i had three different functions do different things with data
return 0;
}
basically what i was missing is to set bool back to false whenever while loop started over this is why data wasn't calculated properly. Anyways Thank you all for help and all the tips!
I'm working on another school project, in part of which I need to take the data from a .txt file and convert it to a mixed type vector... that contains two integers and a string. I've tried various ways of doing it, and I have it down to where the .txt file is inputted, and hopefully sent to vectors. My difficulty comes in when I try to do stuff with the vectors. My code segment is as follows (I'll explain what things are for with comments):
bool ReadPeopleFromFile(int argc, char* argv[], vector<Person> &people) {
Person tmpPrsn;
int tmpAge;
string tmpGender;
int tmpAnualIncome;
ifstream PeopleFile("dev_people.txt"); // Try to open file
if (!PeopleFile.is_open()) {
cout << "Could not open file.\n";
return true; // indicates error
}
cout << "Starting" << endl;
while (!PeopleFile.eof()) {
PeopleFile >> tmpAge;
PeopleFile >> tmpGender;
PeopleFile >> tmpAnualIncome;
tmpPrsn.SetData(tmpAge, tmpGender, tmpAnualIncome);
tmpPrsn.Print();
people.push_back(tmpPrsn); // Need to look at this!!!
}
PeopleFile.close();
cout << "Finished reading file." << endl;
return false;
}
//I have a function that gets the user input... I took it out for this post.
vector<Person> ptntlCstmrs;
// Return people within the given age range.
vector<Person> GetPeopleWithQualifyingCharacteristics(vector<Person> ppl, int AgelowerRange, int AgeupperRange, string DesiredGender, int YIlowerRange, int YIupperRange) {
unsigned int i = 0;
unsigned int j = 0;
unsigned int k = 0;
vector<Person> pplInRange;
int age = 0;
string gender = "";
int yearlyIncome = 0;
for (i = 0; i < ppl.size(); ++i) {
for (j = 0; j < ppl.size(); ++j) {
for (k = 0; k < ppl.size(); ++k) {
age = ppl.at(i).GetAge();
gender = ppl.at(j).GetGender();
yearlyIncome = ppl.at(k).GetYearlyIncome();
if ((age >= AgelowerRange) && (age <= AgeupperRange) && (gender == DesiredGender || gender == "Any") && (yearlyIncome >= YIlowerRange) && (yearlyIncome <= YIupperRange)) {
ptntlCstmrs.push_back(ppl.at(i));
}
}
} // I know this section is messed up... I can't figure out how to get this part to work. What I'm trying to do is take the input, and filter it based on the user-inputted criteria. Nothing that I have done has worked.
}
return pplInRange;
}
int main(int argc, char* argv[]) {
vector<Person> ptntlCstmrs;
bool hadError = false;
int ageLowerRange = 0;
int ageUpperRange = 0;
string desiredGender = "";
int yearlyIncomeLowerRange = 0;
int yearlyIncomeUpperRange = 0;
hadError = ReadPeopleFromFile(argc, argv, ptntlCstmrs);
if (hadError) {
return 1; // indicates error
}
GetUserInput(ageLowerRange, ageUpperRange, desiredGender, yearlyIncomeLowerRange, yearlyIncomeUpperRange);
ptntlCstmrs = GetPeopleWithQualifyingCharacteristics(ptntlCstmrs, ageLowerRange, ageUpperRange, desiredGender, yearlyIncomeLowerRange, yearlyIncomeUpperRange);
cout << "\nNumber of potential customers = " << ptntlCstmrs.size() << endl;
}
I included a bunch of this stuff just in case some of my references are off, but I don't think so. It also tells you what I'm working with. I would like help figuring out how to get the criteria to actually sort the vectors, and then print out how many of those vectors are going to work with the user inputted data.
Thanks in advance!
The credit really goes to #PaulMcKenzie . The problem with the above code is in the following section:
Return people within the given age range.
vector<Person> GetPeopleWithQualifyingCharacteristics(vector<Person> ppl, int AgelowerRange, int AgeupperRange, string DesiredGender, int YIlowerRange, int YIupperRange) {
unsigned int i = 0;
unsigned int j = 0;
unsigned int k = 0;
vector<Person> pplInRange;
int age = 0;
string gender = "";
int yearlyIncome = 0;
for (i = 0; i < ppl.size(); ++i) {
for (j = 0; j < ppl.size(); ++j) {
for (k = 0; k < ppl.size(); ++k) {
age = ppl.at(i).GetAge();
gender = ppl.at(j).GetGender();
yearlyIncome = ppl.at(k).GetYearlyIncome();
if ((age >= AgelowerRange) && (age <= AgeupperRange) && (gender == DesiredGender || gender == "Any") && (yearlyIncome >= YIlowerRange) && (yearlyIncome <= YIupperRange)) {
ptntlCstmrs.push_back(ppl.at(i));
}
}
} // I know this section is messed up... I can't figure out how to get this part to work. What I'm trying to do is take the input, and filter it based on the user-inputted criteria. Nothing that I have done has worked.
}
return pplInRange;
which needs to be changed to the following:
vector<Person> GetPeopleWithQualifyingCharacteristics(vector<Person> ppl, int Agelow, int Ageup, string DesGen, int YIlow, int YIup) {
unsigned int i = 0;
int age = 0;
string gender = "";
int yearlyIncome = 0;
ifstream PeopleFile("dev_people.txt");
while (PeopleFile >> age >> gender >> yearlyIncome);
vector<Person> pplInRange;
for (i = 0; i < ppl.size(); ++i) {
age = ppl.at(i).GetAge();
gender = ppl.at(i).GetGender();
yearlyIncome = ppl.at(i).GetYearlyIncome();
if ((age >= Agelow) && (age <= Ageup) && (DesGen == gender || DesGen == "Any") && (yearlyIncome >= YIlow) && (yearlyIncome <= YIup)) {
pplInRange.push_back(ppl.at(i));
}
}
return pplInRange;
}
Now it works fine. Thanks Paul!
My initial thoughts are that you aren't collecting user input correctly. You send your variables that determine whether or not a Person object is a potential customer off into another function.
By default C++ passes parameters by value -- meaning that when a parameter is passed into a function, a copy of said parameter is made and used throughout that function. When the function comes to a close, the edited parameter is then disposed of and all changes / edits are lost. If you want to grab user input from another method, and have said input saved, make sure to pass your parameters by reference.
Passing by reference essentially tells the machine where that specific parameter is located in memory and allows the program to directly access that piece of memory as opposed to making a copy of the parameter when executing your function. This in turn makes any changes made to parameters permanent!
Hope this helps!