Store 2D array row in parallel array - c++

I'm a beginner to C++, in my homework i have to save a row from a 2D array based on the inputted username and password. The saved row is to be a reference as to where the user info can be found in the 2D array. I haven't been able to figure out how to save the specific row to an integer. The whole code is displayed and the function to preform the task is called validateUser.
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <iomanip>
using namespace std;
void showAll(string theAccounts[5][7]);
void sortInput(string theAccounts[5][7]);
bool validateUser(string theAccounts[5][7], string username, string password, int &saveRow);
bool readFile(string theAccounts[5][7]);
int main()
{
//Declare Necessary Variables and 2D array to store data from input file.
string username;
string password;
string accountData[5][7] = { " " };
bool userGood;
bool fileGood;
//Call the readFile function and capture the returned value.
fileGood = readFile(accountData);
//Test if file was successfully read, if so continue with program else exit with error message
if (fileGood == false)
{
cout << "Error occurred...File Unread...Program Exiting" << endl;
}
else
{
cout << "\nFile Read Successfully...\n\n" << endl;
//Ask user to Enter User Name or Zero to Exit
cout << "Enter the following information or 0 to Exit...\n";
cout << "Please Enter Your User Name > ";
//Read in User Name
//If User Name read in is “zero” Exit program.
cin >> username;
if (username == "0")
{
return 0;
}
//Ask the user to Enter Their Password or Zero to Exit
cout << "Please Enter Your User Password > ";
//Read in User Password
//If User Password read in is “zero” Exit program
cin >> password;
if (password == "0")
{
return 0;
}
//Call the Validate User function and capture the returned value
//If returned value from the Validate User function is TRUE continue program to check access code if U or A
//If U – code appropriately
//If A – code appropriately
//Else if returned value Not Valid from the Validate User function, FALSE, output message username and password invalid
}
return 0;
}
void showAll(string theAccounts[5][7])
{
const int Rows = 5;
const int Columns = 7;
ifstream accountFile;
accountFile.open("AccountData.txt");
if (accountFile.is_open())
{
for (int i = 0; i < Rows; i++)
{
for (int j = 0; j < Columns; j++)
{
cout << setw(8) << theAccounts[i][j];
}
cout << endl;
}
}
accountFile.close();
}
void sortInput(string theAccounts[5][7])
{
}
bool validateUser(string theAccounts[5][7], string username, string password, int &saveRow)
{
bool passed = false;
//test for username and password match whats stored in theAccounts
for (int i = 0; i < 5; i++)
{
if (username == theAccounts[i][0] && password == theAccounts[i][3])
{
saveRow = theAccounts[i];
}
}
return passed;
}
bool readFile(string theAccounts[5][7])
{
bool fileRead;
const int Height = 5;
const int Width = 7;
ifstream inputFile; //an ifstream object – input file stream object
inputFile.open("AccountData.txt"); // the open member function opens the text file and links it with
// inputFile to read data from the AccountData.txt file
//input validation to see if file opened
//if opened, read strings into theAccounts array - reset fileRead to true
if (inputFile.is_open())
{
fileRead = true;
for (int i = 0; i < Height; i++)
{
for (int j = 0; j < Width; j++)
{
inputFile >> theAccounts[i][j];
}
}
}
else
{
fileRead = false;
}
inputFile.close();
return fileRead;
}
The .txt file contains:
bham#gnet.com Blake Ham squid62 1987 U Teacher
jdark#att.net Jim Dark gymrat32 1985 A Master
hgreen#lakes.net Hannah Green flower22 2007 U Apprentice
tsmith#dna.com Tom Smith tuna20 2000 U Teacher
jarrow#pnet.com James Arrow ahoy10 2005 U Apprentice

I haven't been able to figure out how to save the specific row to an
integer
In OP function, the specific row is simply i, so all we have to do is to assign it to the variable passed by reference, saveRow and return.
I will not comment the general design of this program as I don't know how much of it is part of the assignment, so I'll use the same interface and data structures OP used:
bool validateUser(string theAccounts[5][7], string username, string password, int &saveRow)
{
//test for username and password match whats stored in theAccounts
for (int i = 0; i < 5; i++)
{
if (username == theAccounts[i][0] && password == theAccounts[i][3])
{
// match found, "save" the right row and return
saveRow = i;
return true;
}
}
// there's no match
return false;
}

For cleaner and easier approach, You should use a combination of std::map and std::vector. std::map<Key, Value> is a container where Keys are all unique and can be of any type you desire. Also Values can be any type you want but can be duplicated. So, imagine it as std::map<Deepblue, information> is an element, another element would be std::map<FirstStep, information> and so on. No two identical keys can be found. Therefore, the Key will be a string and the Information is going to be a vector<string> container of strings.
The below code works and it populates all your data from the input file to the map. Follow the comments and you should see how it works:
#include <iostream>
#include <map>
#include <vector>
#include <fstream>
#include <string>
#include <sstream>
int main()
{
std::map<std::string, std::vector<std::string>> MyMap; // map that has a string key and a vector of strings as information
std::ifstream InFile; // to read from a text file
InFile.open("C:\\....\\input.txt");
if (InFile.fail()) // terminate if failed
{
std::cout << "File did not open correctly!";
return 0;
}
std::string OneLine;
std::vector<std::string> MyVector;
std::string MyKey;
std::vector<std::string> MyValue;
while(std::getline(InFile, OneLine)) // while "reading a whole line" is valid
{
//split the whole line on whitespaces and convert it to a vector of strings
std::stringstream ss(OneLine);
std::string item;
while (getline(ss, item, ' '))
MyVector.push_back(item);
// Now take the first word and set it as the key because it is the email, so it is the "ID" of every line/person
MyKey = MyVector[0];
// Now take the rest of the vector as Value
for (int i = 1; i < MyVector.size(); i++)
MyValue.push_back(MyVector[i]);
// Now add this [Key, Value] pair to the map
MyMap[MyKey] = MyValue;
}
system("pause");
return 0;
}
Now hoover your mouse on this variable to see how it is structured. And do some research on how to access a map write/read etc.

Related

C++: Beginning input into a string vector with a punctuation character terminates program, throws 'std::length_error'

In my program, I have an empty string vector that gets filled in via user input. The program is meant to take numbers from user input, then sort those numbers in order from smallest to largest (the data type is string to make it easier to check for undesired inputs, such as whitespaces, letters, punctuation marks, etc.). If the first character for the first input is a punctuation mark (for example, if the first thing the user submits to the program is ",45"), the program first outputs the following strings I wrote:
Error: Invalid input2.
Please input an integer. When finished providing numbers to organize, input any character that isn't an integer:
and then immediately follows with an error message:
terminate called after throwing an instance of 'std::length_error'
what(): vector::_M_default_append
How can I prevent the error from occurring & terminating the program? Thank you!
#include <iostream>
#include <vector>
#include <algorithm>
#include <limits>
#include <string>
#include <sstream>
#include <stdexcept>
using namespace std;
int main()
{
vector<string> vect; //Container for user inputs. String chosen instead of int to make input filtering easier.
string input; //Variable for user inputs
int intInput; //Variable to convert stringstream to int
int entries = 0; //Variable to track quantity of valid user inputs
int empty = 0; //Variable to track invalid user inputs to be deleted from the vector
int i = 0; //Variable for vector size
int x = 0; //Variable for while loop condition
while (x < 1) //x is permanently 0, loop will only terminate with break statement
{
cout << "Please input an integer. When finished providing numbers to organize, input any character that isn't an integer:\n";
vect.resize(i++);
getline(cin, input);
cout << endl;
stringstream ss(input);
if (ss >> intInput) //Check if stringstream content can be converted to int
{
if (ss.eof()) //Check if stringstream content is only integers
{
vect.push_back(input);
entries++;
}
else
{
cout << "Error: Invalid input1.\n\n";
empty++;
}
}
else if (entries < 1) //Ensures the user submits at least 1 integer before attempting to organize the numbers
{
cout << "Error: Invalid input2.\n\n";
empty++;
i = -1;
continue;
}
else if (entries >= 1)
{
break;
}
}
cout << "All done? Organizing numbers!\n\n";
sort(vect.begin(), vect.end(), [](string a, string b) //Function converts strings to ints, then sorts from smallest to largest
{
int aInt = 0;
int bInt = 0;
try
{
aInt = stoi(a);
}
catch (invalid_argument){}
try
{
bInt = stoi(b);
}
catch (invalid_argument){}
return aInt < bInt;
});
vect.erase (vect.begin(), vect.begin() + empty); //Erases lines of whitespace left by invalid user inputs
for (int j = 0; j < vect.size(); j++)
{
cout << vect[j] << endl;
}
}

C++: Hangman game

I'm in the process of writing the program and stumbled upon a few issues.
The code reads a text file, which contains a list of 20 string words. The function playHangman() is supposed to read the file and randomly picks one word, which is displayed as asterisks in the console. The code works fine with local words when the function is called. For example Playhangman("stackOverflow"), will show the exact number of characters and will loop through them until the word is guessed right. If you take a look at the code, I'm calling the random word into the function. That word is stored as array. I know that that's not the proper way to do randomize, but for now, even if it picks the same word over and over, that's Ok, I just need to make sure it actually reads the array. The other thing is, when all characters are revealed, all the words on that text file are displayed, looks like I'm calling the entire content of the array instead of just that random word that's supposed to be generated.
Any help would be appreciated, thank you!
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
string word[20];
string randomWord = word[rand() % 20];
int playHangman(string randomWord) {
int misses = 0;
int revealedletters = 0;
string display = randomWord;
ifstream textFile;
// Open file
textFile.open("hangman.txt");
// Check if file exists
if (!textFile) {
cerr << "Unable to open text file\n";
exit(1); // Call system to stop
}
else {
cout << "File opened successfully. Program will continue...\n\n";
// Loop through the content of the file
while (textFile >> randomWord) {
for (int i = 0; i < display.length(); i++)
display[i] = '*';
while(revealedletters < randomWord.length()) {
cout << "Misses: " << misses << endl;
cout << "Enter a letter in word ";
cout << display << " : ";
char response;
cin >> response;
bool goodGuess = false;
bool duplicate = false;
for (int i = 0; i < randomWord.length(); i++)
if (response == randomWord[i]) {
if (display[i] == randomWord[i]) {
cout << response << " is already in the word." << endl;
duplicate = true;
break;
}
else {
display[i] = randomWord[i];
revealedletters++;
goodGuess = true;
}
}
if (duplicate)
continue;
if (!goodGuess) {
misses++;
cout << response << " is not in word\n";
}
}
cout << "You guessed right! The word is " << randomWord << ".\n";
}
return misses;
}
}
// TODO: Do you want to guess another word, Y/N?
int main () {
playHangman(randomWord);
// TODO: number of misses to guess the word.\n";
}
In your declaration and initialisation of your global variables:
string word[20];
string randomWord = word[rand() % 20];
word is an array of 20 empty strings and therefore randomWord will also always be empty.
In your playHangman function you have:
while (textFile >> randomWord) {
for (int i = 0; i < display.length(); i++)
display[i] = '*';
while(revealedletters < randomWord.length()) {
......
This reads a single word from the file into randomWord, plays the game with that word then loops round to read the next word and plays again with that word. As revealedletters doesn't get reset the game will finish immediately if the first word is longer than the next one.
I think the code you actually want looks something like this (remove your global variables too):
std::string word;
std::vector<std::string> words;
while (textFile >> word) {
words.push_back(word);
}
randomWord = words[rand() % words.size()];
std::string display = std::string(randomWord.size(), '*');
while(revealedletters < randomWord.length()) {
......
If you really must use arrays:
const size_t maxWords = 20;
std::string words[maxWords];
size_t wordCount = 0;
while (textFile >> word && wordCount < maxWords) {
words[wordCount++] = word;
}
randomWord = words[rand() % wordCount];
std::string display = std::string(randomWord.size(), '*');
while(revealedletters < randomWord.length()) {
......

Extract multiple records from a Text File after keyword search in a specific field

I'm working on a bit of code that will search tickets from a text file. Each ticket starts with "***" and ends with "###".
(1) Search a keyword from user input in the "City" field.
(2) Return the all records and lines from that record once found, not just one.
The record looks like this:
***
Ticket Number :
First Name : T
First Name : C
Address of Violations : 123 Malberry Ln.
City : Oak Park //Need user to input City and Pull all tickets from that City
Plate : Q1234
Vin Number : V1234
Violation Narrative : NO PARKING
Violation Code :V1234
Violation Amount :50
Late Fee : 100
Make :
Model :
Was this Paid ? : 0
Date : 1012017
Time : 1200
Pay by Date : 1012018
Officer Number : 6630
###
My queries have to be specific for each category.
For example, If there are three records and two are from the same city "Oak Park" I want the two "Oak Park" records to display, not just one.
How I'm starting to work this out is doing a search that pulls the record from "***" to "###" possibly like this:
{
ifstream in("tickets.txt");
string beforeEqual = "";
string afterEqual = "";
while (!in.eof())
{
getline(in, beforeEqual, '***'); //grtting string upto =
getline(in, afterEqual, '###'); //getting string after =
cout << afterEqual << endl; //printing string after =
}
But I also need to incorporate a search for ("City : " + search_token).
So when I do a (City Search) it pulls all the tickets from that city.
How would this be implemented and am I on the right track?
My work so far, just a generic search:
void city_search() {
string search;
string line;
ifstream inFile;
inFile.open("tickets.txt");
if (!inFile) {
cout << "Unable to open file" << endl;
exit(1);
}
cout << "Please enter a plate Number to search : " << endl;
cin >> search;
size_t pos;
while (inFile.good())
{
getline(inFile, line); // get line from file
pos = line.find(search); // search
if (pos != string::npos) // string::npos is returned if string is not found
{
cout << search << "Found!";
break;
}
}
I think you'd rather make a generic function for them all. Switches would mean more overhead in the case you describe, if I understood what you were planning initially, correctly.
What you'd rather do is something akin to this:
//This function compares the ticket's category's contents with the given string
std::string checkString(std::string stringToCheck) {
std::string contentDelimiter = " : "; //Text after this will be read in
std::string content = stringToCheck.substr(stringToCheck.find(contentDelimiter) +
contentDelimiter.length(), stringToCheck.length());
//Find where the end of " : " is, read from there 'til end of line into "content"
return content;
}
//This function opens the file, compares sought info to each relevant column,
//and types matching tickets out
void searchFile(const std::string& filePath, const int& amountOfCategories,
const std::string& matchInfo, const int& categoryIndex) {
int i = 0;
std::string tempTicket[amountOfCategories];
//Make a template to copy each ticket to for later use
std::ifstream inFile;
inFile.open(filePath.c_str());
while (!inFile.eof()) { //Go through entire file
std::getline(inFile, tempTicket[i]); //Put next line into the relevant slot
i++;
if (i == amountOfCategories) {
if (matchInfo == checkString(tempTicket[categoryIndex]) {
for (int j = 0; j < amountOfCategories; j++) {
//If you want it to skip the *** and the ###,
//you change it to this: int j = 1; j < amountOfCategories-1;
std::cout << tempTicket[j] << std::endl;
}
std::cout << std::endl;
}
i = 0;
}
}
inFile.close();
}
Also you'll have to make the function finding category indexes, and the static string array of categories to cycle through when searching through them for the correct index. Something like this:
//This outside of main obviously as an independent function
int position(const std::string& soughtCategory,
const std::string arr[], const int& amountOfCategories) {
int found = -1;
for (int i = 0; i<amountOfCategories; i++) {
if (arr[i] == soughtCategory) {
found = i;
}
}
return found;
}
//This in main:
int amountOfCategories = 10; //Or however many there will be(no duplicates)
std::string categories[amountOfCategories];
//categories[0] = "***";
//Place all of the others between these manually
//categories[9] = "###";
//Then call it like this, for example, in main:
searchFile("file.txt", amountOfCategories, "Tampa", position("City", categories, amountOfCategories));

Why does this while loop output the same thing no matter what you input?

I have a program that does three things. Asks you how many variables you wan't, ask you to input each variable, then stores it in a vector. I have put some code that checks if your input is correct, and if it isn't, re-loops the code asking for your variable. The problem I am having is that when you type anything in around the second variable, it asks you to try again infinitely.
For instance, if I typed these values into the input:
Variable amount: 5
Please input variable 1: 8
Please input variable 2: 8
ERROR, PLEASE ENTER ONLY VALID SYMBOLS
---------------------
Please input variable 2:
It would keep outputting ERROR, PLEASE ENTER ONLY VALID SYMBOLS over and over again no matter what you typed. The code is down below, and if you have a better name for this question please let me know. (I'm not really sure what to call this)
#include <iostream>
#include <cmath>
#include <string>
#include <algorithm>
#include <vector>
#include <sstream>
using namespace std;
int inputErrorMessage()
{
cout << "\n ERROR, PLEASE ENTER ONLY VALID SYMBOLS \n";
cout << "--------------------- \n";
return 0;
}
int main()
{
// Declare the variables, vectors, etc.
int varNum = 1;
int totVar = 0;
int choice = 0;
vector<int> userNums;
double input = 0;
string checktotVar = "";
string checkInput = "";
string sym = "";
bool valid = false;
stringstream sstotVar;
stringstream ssinput;
if (choice != 6) {
while (!valid) {
valid = true;
// Ask user for how many variables they want then record it
cout << "Variable amount: ";
getline(cin, checktotVar);
sstotVar << checktotVar;
sstotVar >> totVar;
if (sstotVar.fail() || totVar <= 0) {
inputErrorMessage();
valid = false;
sstotVar.clear();
sstotVar.ignore();
}
}
valid = false;
while (!valid) {
valid = true;
// Ask the user for each variable, then record it into the array
for (int i = 0; i < totVar; ++i) {
cout << "Please input variable " << varNum << ": ";
getline(cin, checkInput);
ssinput << checkInput;
ssinput >> input;
if (ssinput.fail()) {
inputErrorMessage();
valid = false;
ssinput.clear();
ssinput.ignore();
}
if (valid == true) {
userNums.push_back(input);
varNum++;
}
}
}
}
}
ssinput >> input;
reads the one thing in ssinput right to the end of the stream while leaving the read valid. The next time around
ssinput << checkInput;
can't write into the stream because the stream hit the stream's end. That means the read also fails and
if (ssinput.fail()) {
enters the body of the if where the program clears the error
ssinput.clear();
and then promptly reads off the end of the stream with
ssinput.ignore();
causing the error all over again.
Quickest solution:
Recreate
stringstream ssinput;
on each loop iteration. So
stringstream sstotVar;
//stringstream ssinput; gone from here
and
getline(cin, checkInput);
stringstream ssinput(checkInput); // and now tighter scope recreated each loop.
ssinput >> input;
Also by keeping the stream around without emptying it out it can get very., very big.
You can also simplify your logic around
while (!valid) {
and eliminate some repeated code by moving the read validation into it's own function
int getMeANumber(const std::string & message, int min)
that loops until it gets a number and then returns that number. For example:
int getMeANumber(const std::string & message, int min)
{
while (true)
{
cout << message;
string checktotVar;
getline(cin, checktotVar);
stringstream sstotVar(checktotVar);
int totVar;
sstotVar >> totVar;
if (!sstotVar || totVar <= min)
{
inputErrorMessage();
}
else
{
return totVar;
}
}
}
Now main is this itty-bitty tiny lil' thing.
int main()
{
int choice = 0;
vector<int> userNums;
if (choice != 6)
{
int totVar = getMeANumber("Variable amount: ", 0);
for (int i = 0; i < totVar; ++i)
{
stringstream varname;
varname << "Please input variable " << i+1 << ": ";
userNums.push_back(getMeANumber(varname.str(), numeric_limits<int>::min()));
// numeric_limits<int>::min requires #include <limits>
}
}
}
Here are the issues with this code.
In this part:
if (valid == true) {
userNums.push_back(input);
varNum++;
}
you forgot to add an ssinput.clear(). This will reset the stream state (clear the error flags), otherwise you cannot use it again. That is why it stops working at the second input.
In addition, even though this works, you are pushing back a variable that you declared as double into a vector of ints. That is bound to cause issues if this was intended to store double variables, instead of truncating them and storing them as ints.
It should be:
#include <iostream>
#include <cmath>
#include <string>
#include <algorithm>
#include <vector>
#include <sstream>
using namespace std;
int inputErrorMessage()
{
cout << "\n ERROR, PLEASE ENTER ONLY VALID SYMBOLS \n";
cout << "--------------------- \n";
return 0;
}
int main()
{
// Declare the variables, vectors, etc.
int varNum = 1;
int totVar = 0;
int choice = 0;
vector<int> userNums;
double input = 0;
string checktotVar = "";
string checkInput = "";
string sym = "";
bool valid = false;
stringstream sstotVar;
stringstream ssinput;
if (choice != 6) {
while (!valid) {
valid = true;
// Ask user for how many variables they want then record it
cout << "Variable amount: ";
getline(cin, checktotVar);
sstotVar << checktotVar;
sstotVar >> totVar;
if (sstotVar.fail() || totVar <= 0) {
inputErrorMessage();
valid = false;
sstotVar.clear();
sstotVar.ignore();
}
}
valid = false;
while (!valid) {
valid = true;
// Ask the user for each variable, then record it into the array
for (int i = 0; i < totVar; ++i) {
cout << "Please input variable " << varNum << ": ";
getline(cin, checkInput);
ssinput << checkInput;
ssinput >> input;
if (ssinput.fail()) {
inputErrorMessage();
valid = false;
}
if (valid == true) {
userNums.push_back(input);
varNum++;
}
ssinput.clear();
}
}
}
}
EDIT: You need to clear the stringstream on each iteration of the loop, otherwise you're not writing to an empty stream when you grab the next input from the user, which is what's causing the .fail() method to return true after the first iteration of the loop.

Phone Number Lsit

Write a simple telephone directory program in C++ that looks up phone numbers in a file containing a list of names and phone numbers. The user should be prompted to enter a first name and last name, and the program then outputs the corresponding number, or indicates that the name isn't in the directory. After each lookup, the program should ask the user whether they want to look up another number, and then either repeat the process or exit the program. The data on the file should be organized so that each line contains a first name, a last name, and a phone number, separated by blanks. You can return to the beginning of the file by closing it an opening it again.
I cant get the line
check = strstr(phoneDirectory, name);
to work strstr part keep giving the error: no instance of overloaded function "strstr" matches the argument list argument types.
Here a copy of my code:
#include <iostream>
#include <string>
#include <fstream>
#include <cstring>
using namespace std;
int arraySize();
int main()
{
const int SIZE = arraySize();
char *phoneDirectory;
int size=0;
char name; //name to look for
char *check = NULL;
bool find = false;
phoneDirectory = new char [SIZE];
ifstream phoneNumbers;
phoneNumbers.open("phoneNumbers.txt");
if (!phoneNumbers)
cout << "Error opening data file\n";
//looping throught the name file
else
{
for (int i = 0; i < SIZE; i++)
{
phoneNumbers >> phoneDirectory;
}
phoneNumbers.close(); //closes data file
}
phoneNumbers.close();
// Get a name or partial name to search for.
cout << "Enter a name or partial name to search for: ";
cin.getline(phoneDirectory, name);
cout << "\nHere are the results of the search: " << endl;
int entries = 0;
for (int i = 0; i < size; i++)
{
check = strstr(phoneDirectory, name);
if (check != NULL)
find = true;
}
if (!find)
cout << "No matches!" << endl;
delete [] phoneDirectory;
return 0;
}
int arraySize()
{
string phoneNum;
int size = 0;
ifstream phoneNumbers; // Input file stream object
// Open the data file.
phoneNumbers.open("phoneNumbers.txt");
if (!phoneNumbers)
cout << "Error opening data file\n";
//looping throught the name file
else
{
while (getline(phoneNumbers, phoneNum))
{
size++;
}
phoneNumbers.close(); //closes data file
}
return size;
}
Your name variable is a char. Should it be a char* instead?