Unable to Process Data from Text File in C++ - c++

Why is it that this program will not register the correct ID and Pin number entered?
Anything typed will proceed to wrong data being input but the correct data doesn't get recognized.
Inside the text file is 5 IDs, and 5 Pin numbers in a format of 5 rows 2 columns.
#include<iostream>
#include<iomanip>
using namespace std;
void main()
{
const int MAX=10, screenWidth=80;
string A = "Welcome to ABC Bank!";
int i=0;
int ID[MAX], Password[MAX], pin, acc, counter=1 ,limit=2;
cout<<setw((screenWidth-A.size())/2)<<" "<<A<<endl;
cout<<"\nAccount ID: ";
cin>>acc;
cout<<"Pin: ";
cin>>pin;
ifstream accountFile;
accountFile.open("AccountDetails.txt");
if (!accountFile)
cout<<"Unable to open requested file!";
else
{
while (!accountFile.eof())
{
accountFile>>ID[i]>>Password[i];
i++;
}
accountFile.close();
while (acc==ID[i] && pin==Password[i])
{
cout<<"Login successful!\n";
break;
}
while (acc!=ID[i] || pin!=Password[i])
{
if (counter==3)
{
cout<<"\nUnauthorized Access Detected. Account has been LOCKED!\n";
break;
}
else
{
cout<<"\nWrong Account ID/Pin. Please try again!"<<" (Attempts Left:"<<limit<<")";
cout<<"\nAccount ID: ";
cin>>acc;
cout<<"Pin: ";
cin>>pin;
counter++;
limit--;
}
}
}
system("pause");
}
After reviewing what #Joachim Pileborg stated in his answer earlier,
this is the updated code that I have done. Sadly, now this code fails to login successfully after the 1st attempt fails and the 2nd attempt is correct.
ifstream accountFile;
accountFile.open("AccountDetails.txt");
if (!accountFile)
cout<<"Unable to open requested file!";
else
{
while (accountFile>>ID[i]>>Password[i])
{
i++;
}
accountFile.close();
bool success = false;
for (int j=0; !success && j<i; j++)
{
if (ID[j] == acc && Password[j] == pin)
success = true;
}
if (success)
cout<<"\nLogin Successful!\n";
else
{
while (!success)
{
cout<<"\nAccount ID/Pin is incorrect. Please try again!"<<" (Attempts Left: "<<limit<<" )";
cout<<"\nAccount ID: ";
cin>>acc;
cout<<"Pin: ";
cin>>pin;
counter++;
limit--;
if (counter==3)
{
cout<<"Unauthorized Access Detected! Account Has Been LOCKED!\n";
break;
}
}
}
}
system("pause");

The logic for your login success/failure checks are flawed. To begin with they will invoke undefined behavior because you will access uninitialized elements of the arrays.
If, as you say, the file contains five entries, then after the loop i will have the value 5 which is the sixth element in the arrays (after you fix the reading loop, otherwise the value of i will be 6).
If we then overlook the UB (Undefined Behavior) the first loop, checking for successful login, that condition will most likely never be true, and that's good because otherwise you would have an infinite loop there. Then comes the second loop where you check for unsuccessful login, where the condition will almost always be true, and that will lead to an infinite loop.
To check if the login credentials given by the user was correct or not, I suggest something like
bool success = false;
for (int j = 0; !success && j < i; ++j)
{
if (ID[j] == acc && Password[j] == pin)
{
success = true;
}
}
if (success)
{
// Login successful
}
else
{
// Login failed
}

using namespace std;
int main()
{
const int MAX=10, screenWidth=80;
string A = "Welcome to ABC Bank!";
int i=0;
int ID[MAX], Password[MAX], pin, acc, counter=1 ,limit=2;
cout<<setw((screenWidth-A.size())/2)<<" "<<A<<endl;
cout<<"\nAccount ID: ";
cin>>acc;
cout<<"Pin: ";
cin>>pin;
ifstream accountFile;
accountFile.open("AccountDetails.txt");
if (!accountFile)
cout<<"Unable to open requested file!";
else
{
while (accountFile>>ID[i]>>Password[i])
{
i++;
}
accountFile.close();
bool success = false;
while (!success)
{
for (int j=0; !success && j<i; j++)
{
if (ID[j] == acc && Password[j] == pin)
success = true;
}
if (success)
cout<<"\nLogin Successful!\n";
else
{
cout<<"\nAccount ID/Pin is incorrect. Please try again!"<<" (Attempts Left: "<<limit<<" )";
cout<<"\nAccount ID: ";
cin>>acc;
cout<<"Pin: ";
cin>>pin;
counter++;
limit--;
}
if (counter==4)
{
cout<<"Unauthorized Access Detected! Account Has Been LOCKED!\n";
break;
}
}
}
system("pause");
}
CODE IS WORKING, Thank you for the input and help Mr Joachim Pileborg!

Related

terminate called after throwing an instance of 'std::logic_error' what(): basic_string::_M_construct null not valid (HELP)

The compiler doesn't run into any errors, but as soon as my program runs, it hits me with this error.
terminate called after throwing an instance of 'std::logic_error' what(): basic_string::_M_construct null not valid
This is the code:
#include<iostream>
#include<iomanip>
#include<string>
#include<conio.h>
#include<stdio.h>
using namespace std;
int prodnum, brandnum;
struct desc{
string name;
float price;
int stock;
int sold;
};
struct assets{
string prodname;
desc arr_description[8] = {0};
};
assets arr_assets[8] = {0};
void login();
int main()
{
login();
system("cls");
cout<<" ***** INVENTORY SYSTEM C++ *****"<<endl
<<"Enter Number of Products for Inventory: ";
cin>>prodnum;
for(int i = 0; i < prodnum; i++){
cout<<"Product "<<i<<": ";
cin>>arr_assets[i].prodname;
cout<<"How many "<<arr_assets[i].prodname<<"? ";
cin>>brandnum;
cout<<endl;
cout<<"Name: ";
cin>>arr_assets[i].arr_description[i].name;
cout<<endl;
cout<<"Price: ";
cin>>arr_assets[i].arr_description[i].price;
cout<<endl;
cout<<"Stock: ";
cin>>arr_assets[i].arr_description[i].stock;
cout<<"Sold: ";
cin>>arr_assets[i].arr_description[i].sold;
}
system("cls");
cout<<" ***** INVENTORY SYSTEM C++ *****\n";
cout<<"Product No."<<setw(7)<<"Product"<<setw(7)<<"Name"<<setw(7)<<"Price"<<setw(7)<<"Stock"<<setw(7)<<"Sold"<<setw(7)<<"Left\n";
for(int i = 0; i < prodnum; i++){
for(int j = 0; j < brandnum; j++){
cout<<" ["<<i<<"] "<<setw(7)
<<arr_assets[i].prodname<<setw(7)
<<arr_assets[i].arr_description[j].name<<setw(7)
<<arr_assets[i].arr_description[j].price<<setw(7)
<<arr_assets[i].arr_description[j].stock<<setw(7)
<<arr_assets[i].arr_description[j].sold<<setw(7)
<<arr_assets[i].arr_description[j].stock - arr_assets[i].arr_description[j].sold;
}
}
}
void login()
{
int attempt = 0;
bool success = false;
string correctU = "admin";
string correctP = "Exer04\r";
string username, password;
char pword[100];
while(attempt < 3 && !success){
system("cls");
cout<<"Enter Username: ";
cin>>username;
cout<<"Enter Password: ";
for(int i = 0; i < 100; i++){
pword[i] = _getch();
_putch('*');
if(pword[i] == 13){
break;
}
}
password = pword;
cout<<endl;
try{
if(username != correctU){
throw invalid_argument("Invalid Username");
}
if(password != correctP){
throw invalid_argument("Invalid Password");
}
if(username != correctU && password != correctP){
throw invalid_argument("Invalid Username and Password");
}
success = true;
}
catch(const invalid_argument& e){
cout<<"Error: "<<e.what()<<endl;
system("pause");
continue;
attempt++;
}
}
if(!success){
cout<<"Maximum Attempts Reached...";
exit(0);
}
}
I don't exactly know if its the structures that are the problem or I declared a string wrong because I've been looking for it for a while now and I can't seem to find the problem myself. The compiler didn't have any problems with the code. It compiled with no errors or warnings but the programs just doesn't run because of this error.
Is this code still salvageable? I don't know what's causing it.
Just change this
desc arr_description[8] = {0};
to this
desc arr_description[8];
and this
assets arr_assets[8] = {0};
to this
assets arr_assets[8];
Classes like assets and desc have their default constructors called by default. You don't have to initialise things unless you specifically want a particular initial value.
What your code was doing was using 0 to initialise a string, in that context 0 counts as a null pointer, and that's why you got the error message you did (i.e. 'basic_string::_M_construct null not valid'). It should be clear that it makes no sense (in general) to initialise an object with 0.
No doubt you saw = {0} in a context where it did make sense, but try to get out of the habit of copying code you don't fully understand, that rarely works out.
EDIT
Now here's an irony. As pointed out in the comments your code has another bug in that the pword array is not null terminated which makes assigning it to a string an error. A simple solution to this issue is to write
char pword[100] = {0};
because that would initialise the whole array to zeros and so your array would automatically be null terminated.
A different and perhaps better solution would be to change this code
if (pword[i] == 13) {
break;
}
to this
if (pword[i] == 13) {
pword[i] = 0;
break;
}
which not only adds a null terminator in the right place, but also removes the 13 character from the end of your password. Obviously this also means you would have to change
string correctP = "Exer04\r";
to
string correctP = "Exer04";

c++ validation login code

i am trying to make a simple validation user name and password for my program.
i am not sure whats wrong with my code, but i think i have some logic error
here is my piece of code
string usernameinput,passinput;
string username[]={"mmm","nnnn","rrrr","aaa"};
string password[]={"1234","1212","1234","1212"};
bool flag=false;
while(flag==false){
cout << "username: "<<endl;
cin>>usernameinput;
cout << "password: "<<endl;
cin>>passinput;
for(int i=0;i<=3;i++){
if(usernameinput ==username[i] && passinput==password[i] )
{
flag=true;
}
else if (usernameinput !=username[i] && passinput!=password[i])
{
flag=false;
cout<<"please enter correct username and password"<<endl;
}
}
}
You don't need the second if statement, else will suffice. Even if you keep it, it has to be an || not &&, because either one not matching is a case for setting the flag to false.
In fact, you don't need even the else statement. Since you are initialising the flag to false, it will remain false until it is set to true, which will happen only when username and password both match. Once there is a match, you want to stop comparing, so you have to use break to end the for loop. The error message should be outside the for loop as you don't want it to show until you have checked against all the values.
bool flag=false;
while(flag==false){
cout << "username: "<<endl;
cin>>usernameinput;
cout << "password: "<<endl;
cin>>passinput;
for(int i=0;i<=3;i++){
if(usernameinput ==username[i] && passinput==password[i] )
{
flag=true;
break;
}
}
if(!$flag) {
cout<<"please enter correct username and password"<<endl;
}
}
Problem
The program does not stop searching for a match after a match is found. Since the values tested after the match will not be a match, chaos ensues.
Let's watch that happens when we execute the following code for input of mmm and 1234 for user name and password, respectively:
for(int i=0;i<=3;i++){
if(usernameinput ==username[i] && passinput==password[i] )
{
flag=true;
}
else if (usernameinput !=username[i] && passinput!=password[i])
{
flag=false;
cout<<"please enter correct username and password"<<endl;
}
}
Iteration 1:
if("mmm" == "mmm" && "1234" == "1234" ) // both true. Enter
{
flag=true; // flag is now true
}
else if ("mmm" != "mmm" && "1234" != "1234")
{
flag=false;
cout<<"please enter correct username and password"<<endl;
}
Now, since nothing tells the program that match has been found and we can stop looking, we proceed to iteration 2:
if("mmm" == "nnnn" && "1234" == "1212" ) // false
{
flag=true;
}
else if ("mmm" != "mmm" && "1234" != "1234") // both false, enter
{
flag=false; // flag is now false Ooops.
cout<<"please enter correct username and password"<<endl;
}
Output, even though the credentials were correct, flag will be false and the user will be alerted to the false negative three times. Yuck.
please enter correct username and password
please enter correct username and password
please enter correct username and password
We needed to exit the loop when we got a match. The obvious solution is something like:
if(usernameinput ==username[i] && passinput==password[i] )
{
flag=true;
break;
}
But wait! There's more! What if the input is mmm and 1235?
Iteration 1:
if("mmm" == "mmm" && "1235" == "1234" ) //Second case fails
{
flag=true;
break;
}
else if ("mmm" != "mmm" && "1234" != "1234") // first case fails
{
flag=false;
cout<<"please enter correct username and password"<<endl;
}
We don't enter either case. flag remains false and the intruder doesn't get in, but this just looks ugly and still gives you the three error messages. We can do better.
Solution
Make a function:
bool checkcredentials(const std::string & uname,
const std::string & pword)
{
bool rval = false;
for(int i=0;i<=3;i++)
{
if(uname== username[i])
{
if (pword==password[i])
{
rval = true; // could just return true here, but some folk get uptight
// over multiple returns in a function
}
break;
}
}
return rval;
}
The calling function does something like
if (checkcredentials(usernameinput, passinput))
{
// let the user in.
}
else
{
cout<<"please enter correct username and password"<<endl;
}
Note that the check password function does nothing but check credentials. All of the communicating with the user is done elsewhere. This keeps the function dead simple. It does only one thing, and it is the one thing described by the function name.
Security note: before returning a message to the user wait some small random amount of time. This messes with the heads of anyone who is trying to guess the size of the credentials or the size of the credentials database by timing the response.
This can be cleaned up further by pairing the username with the password and having only one array. This ensures that usernames are matched up with passwords and no one added a user name without adding a password.
pair<string, string> credentials[]={ // array of pairs
{"mmm", "1234"},
{"nnnn", "1212"},
{"rrrr", "1234"},
{"aaa", "1212"}
};
bool checkcredentials(const std::string & uname,
const std::string & pword)
{
bool rval = false;
for(auto & cred: credentials)
{
if(uname == cred.first)
{
if (pword == cred.second)
{
rval = true;
}
break;
}
}
return rval;
}
Documentation on pair.
And this can be improved with a smarter datastructure. map is an associative container that makes looking up a value , password in this case, based on a key, user name.
map<string, string> credentials={
{"mmm", "1234"},
{"nnnn", "1212"},
{"rrrr", "1234"},
{"aaa", "1212"}
};
bool checkcredentials(const std::string & uname,
const std::string & pword)
{
auto cred = credentials.find(uname); // look for user name
if (cred != credentials.end() && // username exists
cred->second == pword) // password matches
{
return true;
}
return false;
}
Documentation on map.

to check type of input in c++

## To check type of data entered in cpp ##
int main()
{
int num;
stack<int> numberStack;
while(1)
{
cin>>num;
if(isdigit(num))
numberStack.push(num);
else
break;
}
return(0);
}
If I declare a variable as interger, and I input an alphabet, say 'B', instead of the number, can I check this behavior of user? My code above exits when first number is entered and does not wait for more inputs.
First of all, the std::isdigit function checks if a character is a digit.
Secondly, by using the input operator >> you will make sure that the input is a number, or a state flag will be set in the std::cin object. Therefore do e.g.
while (std::cin >> num)
numberStack.push(num);
The loop will then end if there's an error, end of file, or you input something that is not a valid int.
First take your input as string
Using builtin libraries like isdigit() classify it as an integer
else if it contains '.'then its a float
else if it a alphanumerical the it is a string thats it
Code for this is below,
#include<iostream>
#include<string.h>
using namespace std;
int isint(char a[])
{
int len=strlen(a);
int minus=0;
int dsum=0;
for(int i=0;i<len;i++)
{
if(isdigit(a[i])!=0)
dsum++;
else if(a[i]=='-')
minus++;
}
if(dsum+minus==len)
return 1;
else
return 0;
}
int isfloat(char a[])
{
int len=strlen(a);
int dsum=0;
int dot=0;
int minus=0;
for(int i=0;i<len;i++)
{
if(isdigit(a[i])!=0)
{
dsum++;
}
else if(a[i]=='.')
{
dot++;
}
else if(a[i]=='-')
{
minus++;
}
}
if(dsum+dot+minus==len)
return 1;
else
return 0;
}
int main()
{
char a[100];
cin>>a;
if(isint(a)==1)
{
cout<<"This input is of type Integer";
}
else if(isfloat(a)==1)
{
cout<<"This input is of type Float";
}
else
{
cout<<"This input is of type String";
}
}
use cin.fail() to check error and clean the input buffer.
int num;
while (1) {
cin >> num;
if (cin.fail()) {
cin.clear();
cin.sync();
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
continue;
}
if (num == -1) {
break;
}
numberStack.push(num);
}

comparing elements of an array in c++

We have a project that simulate the function of an atm. the user must enter a pincode and it will be masked with an asterisk. the input pincode must be equal to the default pincode that is stored in an array. My program can masked the input pincode with an asterisks, the only problem is that even if the input pincode is the same with the default pincode, it still output incorrect. what must be the problem? here is my code:
void checkPword()
{
char defaultPin[4] = "1234";
char inputPin[4] = "";
clrscr();
for (int cnt = 0; cnt <= 3; cnt++)
{
cout << "*";
inputPin[ctr];
}
if (defaultPin[0] == inputPin[0] && defaultPin[1] == inputPin[1]
&& defaultPin[2] == inputPin[2] && defaultPin[3] == inputPin[3])
{
clrscr();
cout << "pincode is correct";
}
else
{
clrscr();
cout << "pincode is incorrect";
}
}
Maybe you have to assign getch() to ctr?
ctr = getch();
Inside for..
PLUS: the instruction
inputPin[ctr];
does not have effects!
You have add:
inputPin[cnt] = putchar(ctr);
SUGGESTION
just to make code clear, replace "cnt" with "i".
SOLUTION
char defaultPin[4]="1234";
char input[4] = "";
char currentChar;
bool pinFail = false;
for(int i=0; i != 3; i++) {
currentChar = getchar();
input[i] = currentChar;
/* In this way you have only 3 if-control, not 3*4 as in your program */
if(currentChar != defaultPin[i]) {
pinFail = true;
}
}
if(pinFail) {
/* do something (print error?) */
} else {
/* coutinue with your application */
}
void checkPword()
{
char defaultPin[4]={1,2,3,4};
char inputPin[4]="";
clrscr();
for(int cnt=0;cnt<=3;cnt++)
{
inputPin[cnt] = getch();
cout<<"*";
}
if ((defaultPin[0]==inputPin[0])&&(defaultPin[1]==inputPin[1])&&(defaultPin[2]==inputPin[2])&&(defaultPin[3]==inputPin[3]))
{
clrscr();
cout<<"pincode is correct";
}
else
{
clrscr();
cout<<"pincode is incorrect";
}
}

C++ program, infinite loop on input

Hello i compile this program to an executable called picodb and try to run it through a shell bash script at linux.
int main(int argc, char** argv) {
Debbuger DB(argc,argv);
char command[12];
int len;
bool valid;
while(1)
{
valid=true;
cin.clear();
cin>>command;
len=strlen(command);
if(command[0]=='t')
{
if(strcmp(command,"trace")==0 || len==1)
{
cin>>command;
valid=DB.Trace(command);
}
else valid=false;
}
else if(command[0]=='r')
{
if(strcmp(command,"redirect")==0 || len==1)
{
char stream[6],file[15];
cin>>stream; cin>>file;
valid=DB.Redirect(stream,file);
}
else valid=false;
}
else if(command[0]=='b')
{
if(strcmp(command,"blocking-mode")==0 || len==1)
{
cin>>command;
valid=DB.Blocking_mode(command);
}
else valid=false;
}
else if(command[0]=='l')
{
if(strcmp(command,"limit-trace")==0 || len==1)
{
int n;
cin>>n;
valid=DB.Limit_Trace(n);
}
else valid=false;
}
else if(command[0]=='g')
{
if(strcmp(command,"go")==0 || len==1)
{
DB.Go(argv[1]) ;
}
}
else if(command[0]=='q')
{
if(strcmp(command,"quit")==0 || len==1)
return 0;
else
valid=false;
}
else if(command[0]=='h')
{
if(strcmp(command,"help")==0 || len==1)
DB.Help();
else valid=false;
}
else valid =false;
if(valid==false)
{
cout<<"Invalid command"<<endl;
}
}
}
The script is this:
#!/bin/bash
./picodb << EOF
t file_management
r stdin input.txt
EOF
but when i execute this script the program goes to an infinite loop where it gets the "t file_management" and never stops or change this input. I printed the string command and it always has the value " t file_management" the first argument. I can't figure what I am doing wrong. I tried running the program at terminal giving it the inputs by hand and works fine.
Edit:To make it clearer this is supposed to be a user interface.It never gets out of loop until you type quit.The problem is that what is read throuth the bash repeats to infinite.
cin >> command will only read one word at a time. Are you sure you have "t file_management" in command? When I run your program it works. However it still ran in an infinite loop using the script, so I changed the following line: while(1) -> while(cin >> command)