You have some basic problems in understanding IO operations.
You need to check the difference between formatted and unformatted input. For example
std::cin >> option;
will read one character, but not the '\n' at the end of the line. A subsequent call to an unformatted input function, will read just the '\n' and nothing more.
Then, you are mixing up std::istream.get()
and std::istream.getline()
. They have a total different behaviour regarding the handling of the "end of line" character '\n'. Please read here and here.
You need to understand the difference.
Then, many additional problems resulting from using plain C-Style arrays and especially not standard C-elements like std::string
. If you would use std::string
then life would be very much simpler.
Anyway, there are many other more syntax and semantic errors, as well as design errors.
Important notice: Please make your design first on a piece of paper and then start coding. Always think many days, before writing the first line of code.
So, then to your code. Lets make it compilable:
#include <iostream>
#include <fstream>
#include <iomanip>
#include <stdlib.h>
#include <string.h>
using namespace std;
#define MAX_USER 100
#define LENGTH_UNIT 30
#define LENGTH_NAME 50
#define LENGTH_MAIL 80
#define MAX_NUM 35
#define LENGTH_IC 50
struct USER {
char unit_number[LENGTH_UNIT];
char name[LENGTH_NAME];
char email[LENGTH_MAIL];
char phone[MAX_NUM];
char IC[LENGTH_IC];
};
void add_new_user(void)
{
system("CLS");
ifstream in_file("information.txt");
USER new_user[40];
int index = 0;
char option;
char unit_number;
if (!in_file)
{
cout << "Error opening txt file" << endl;
}
else
{
USER U_list[MAX_USER];
int index = 0;
fflush(stdin);
in_file.getline(U_list[++index].unit_number, LENGTH_UNIT);
while (in_file)
{
in_file.getline(U_list[index].name, LENGTH_NAME);
in_file.getline(U_list[index].email, LENGTH_MAIL);
in_file.getline(U_list[index].phone, MAX_NUM);
in_file.getline(U_list[index].IC, LENGTH_IC);
if (in_file.peek() == '\n')
in_file.ignore(256, '\n');
in_file.getline(U_list[++index].unit_number, LENGTH_UNIT);
}
in_file.close();
bool in = false;
do
{
char check_unit_number[LENGTH_UNIT];
int unit_number_count = 0;
cout << "==================================================" << endl;
cout << "==\tUser Management System\t\t==" << endl;
cout << "==================================================" << endl;
cout << "==\tAdd New User\t\t\t==" << endl;
cout << "==================================================\n";
fflush(stdin);
cout << "\nEnter unit number of the new user: " << endl;
cin.get(check_unit_number, LENGTH_UNIT);
cin.ignore();
for (int i = 1; i < index; i++)
if (strcmp(check_unit_number, U_list[i].unit_number) == 0)
unit_number = i;
if (strcmp(check_unit_number, U_list[unit_number].unit_number) == 0)
{
fflush(stdin);
cout << "\nEnter new user's unit number: " << endl;
cin.get(new_user[++index].unit_number, LENGTH_UNIT);
cin.clear();
fflush(stdin);
cout << "\nEnter new user's name: " << endl;
cin.get(new_user[index].name, LENGTH_NAME);
cin.clear();
fflush(stdin);
cout << "\nEnter new user's EMAIL ADDRESS: " << endl;
cin.get(new_user[index].email, LENGTH_MAIL);
cin.clear();
fflush(stdin);
cout << "\nEnter new user's phone number: " << endl;
cin.get(new_user[index].phone, MAX_NUM);
cin.clear();
fflush(stdin);
cout << "\nEnter new user's IC: " << endl;
cin.get(new_user[index].IC, LENGTH_IC);
cin.clear();
do
{
cout << "\nDo you confirm to register this new user? [Yes - Y, No - N]\n=>";
cin >> option;
if (option != 'Y' && option != 'N' && option != 'y' && option != 'n')
{
cout << endl;
cout << "Wrong input. Enter again." << endl;
cout << endl;
}
} while (option != 'Y' && option != 'N' && option != 'y' && option != 'n');
if (option == 'Y' || option == 'y')
{
ofstream out_file("information.txt", ios::app);
out_file << setiosflags(ios::left) << new_user[index].unit_number << endl;
out_file << setiosflags(ios::left) << new_user[index].name << endl;
out_file << setiosflags(ios::left) << new_user[index].email << endl;
out_file << setiosflags(ios::left) << new_user[index].phone << endl;
out_file << setiosflags(ios::left) << new_user[index].IC << endl;
out_file.close();
cout << endl;
cout << "The new user is successfully registered.\n" << endl;
system("pause");
return;
}
else if (option == 'N' || option == 'n')
{
cout << "\nThe new user is not registered.\n" << endl;
system("pause");
return;
}
}
else if (strcmp(check_unit_number, U_list[unit_number].unit_number) != 0)
{
cout << "\nThis unit number is not exist.\n" << endl;
system("pause");
return;
}
} while (!in);
}
}
int main() {
add_new_user();
}
So, now it is at least compilable. However, it will not work. There are still compiler warnings:

All errors and warnings must be removed.
Let us start with refactoring.
First a code review. I put all finding as a comment in the source code.
Note: Nearly every line of code has a finding.
#include <iostream>
#include <fstream>
#include <iomanip>
#include <stdlib.h> // Do not use C headers
#include <string.h> // Do not use C headers
using namespace std; // Should never be done in C++. Always use qualified names
#define MAX_USER 100 // Macros should never be used for that purpose in C++. Use constexpr instead
#define LENGTH_UNIT 30 // Macros should never be used for that purpose in C++. Use constexpr instead
#define LENGTH_NAME 50 // Macros should never be used for that purpose in C++. Use constexpr instead
#define LENGTH_MAIL 80 // Macros should never be used for that purpose in C++. Use constexpr instead
#define MAX_NUM 35 // Macros should never be used for that purpose in C++. Use constexpr instead
#define LENGTH_IC 50 // Macros should never be used for that purpose in C++. Use constexpr instead
struct USER {
char unit_number[LENGTH_UNIT]; // C-Style arrays should not be used in C++. Use datatype string instead
char name[LENGTH_NAME];// C-Style arrays should not be used in C++. Use datatype string instead
char email[LENGTH_MAIL];// C-Style arrays should not be used in C++. Use datatype string instead
char phone[MAX_NUM];// C-Style arrays should not be used in C++. Use datatype string instead
char IC[LENGTH_IC];// C-Style arrays should not be used in C++. Use datatype string instead
};
void add_new_user(void)
{
system("CLS"); // Should not be used
ifstream in_file("r:\\information.txt");
static USER new_user[40]; // Do not put this onto the functions stack. Why 40? No need to collect new users locally
// int index = 0; // index is defined again below
char option;
unsigned int unit_number; // Should not be char but unsigned int
if (!in_file)
{
cout << "Error opening txt file" << endl; // Should be written do std::cerr. endl not necesarry
}
else
{
static USER U_list[MAX_USER]; // Should not be put onto stack of function
int index = 0; // Should be unsigned
fflush(stdin); // Should never be done. Will end in undefined behaviour
in_file.getline(U_list[++index].unit_number, LENGTH_UNIT); // Should be optimized
while (in_file)
{
in_file.getline(U_list[index].name, LENGTH_NAME);
in_file.getline(U_list[index].email, LENGTH_MAIL);
in_file.getline(U_list[index].phone, MAX_NUM);
in_file.getline(U_list[index].IC, LENGTH_IC);
if (in_file.peek() == '\n') // Why that?
in_file.ignore(256, '\n');
in_file.getline(U_list[++index].unit_number, LENGTH_UNIT); // Bad index handling
}
in_file.close(); // No need. Destructor will close file for you. But, does not harm
bool in = false; // Starting an endless loop and jumping out with return. Bad design
do
{
char check_unit_number[LENGTH_UNIT];// C-Style arrays should not be used in C++. Use datatype string instead
// int unit_number_count = 0; This variable is used nowhere // endl not necessary. \n can be part of string
cout << "==================================================" << endl; // endl ist not necessary. All the following can be done in one statement.
cout << "==\tUser Management System\t\t==" << endl;
cout << "==================================================" << endl;
cout << "==\tAdd New User\t\t\t==" << endl;
cout << "==================================================\n";
fflush(stdin); // Should never be done. Will end in undefined behaviour
cout << "\nEnter unit number of the new user: " << endl; // endl not necessary. \n can be part of string
cin.get(check_unit_number, LENGTH_UNIT);
cin.ignore(); // ???
for (int i = 0; i < index; i++) // array indices start with 0 in c++
if (strcmp(check_unit_number, U_list[i].unit_number) == 0)
unit_number = i; // **** This will result in undefined and bad behaviour
// Because, unit number may never be initialized and that it has an undefined (random) value
// Or the value from the last loop run. In any case: wrong
if (strcmp(check_unit_number, U_list[unit_number].unit_number) == 0) // **** May leed to out of bounds undefined beahviour
{
fflush(stdin); // Should never be done. Will end in undefined behaviour
cout << "\nEnter new user's unit number: " << endl; // endl not necessary. \n can be part of string
cin.get(new_user[++index].unit_number, LENGTH_UNIT);
cin.clear(); // Better handling of errors necessary
fflush(stdin); // Should never be done. Will end in undefined behaviour
cout << "\nEnter new user's name: " << endl; // endl not necessary. \n can be part of string
cin.get(new_user[index].name, LENGTH_NAME);
cin.clear(); // Better handling of errors necessary
fflush(stdin); // Should never be done. Will end in undefined behaviour
cout << "\nEnter new user's EMAIL ADDRESS: " << endl; // endl not necessary. \n can be part of string
cin.get(new_user[index].email, LENGTH_MAIL);
cin.clear(); // Better handling of errors necessary
fflush(stdin); // Should never be done. Will end in undefined behaviour
cout << "\nEnter new user's phone number: " << endl; // endl not necessary. \n can be part of string
cin.get(new_user[index].phone, MAX_NUM);
cin.clear(); // Better handling of errors necessary
fflush(stdin); // Should never be done. Will end in undefined behaviour
cout << "\nEnter new user's IC: " << endl; // endl not necessary. \n can be part of string
cin.get(new_user[index].IC, LENGTH_IC);
cin.clear(); // Better handling of errors necessary
do
{
cout << "\nDo you confirm to register this new user? [Yes - Y, No - N]\n=>";
cin >> option;
if (option != 'Y' && option != 'N' && option != 'y' && option != 'n')
{
cout << endl; // endl not necessary. \n can be part of string
cout << "Wrong input. Enter again." << endl; // endl not necessary. \n can be part of string
cout << endl; // endl not necessary. \n can be part of string
}
} while (option != 'Y' && option != 'N' && option != 'y' && option != 'n');
if (option == 'Y' || option == 'y')
{
ofstream out_file("information.txt", ios::app);
out_file << setiosflags(ios::left) << new_user[index].unit_number << endl;
out_file << setiosflags(ios::left) << new_user[index].name << endl;
out_file << setiosflags(ios::left) << new_user[index].email << endl;
out_file << setiosflags(ios::left) << new_user[index].phone << endl;
out_file << setiosflags(ios::left) << new_user[index].IC << endl;
out_file.close();
cout << endl; // endl not necessary. \n can be part of string
cout << "The new user is successfully registered.\n" << endl; // endl not necessary. \n can be part of string
system("pause"); // Should not be used
return; // Like a goto. Bad design
}
else if (option == 'N' || option == 'n')
{
cout << "\nThe new user is not registered.\n" << endl; // endl not necessary. \n can be part of string
system("pause"); // Should not be used
return; // Like a goto. Bad design
}
}
else if (strcmp(check_unit_number, U_list[unit_number].unit_number) != 0)
{
cout << "\nThis unit number is not exist.\n" << endl; // endl not necessary. \n can be part of string
system("pause"); // Should not be used
return; // Like a goto.Bad design
}
} while (!in); // Using an endless loop and jumping out with return. Bad design
}
}
int main() {
add_new_user();
}
Next. Let's remove the hard bugs and the findings. Much better. But: The result will still not work as expected:
#include <iostream>
#include <fstream>
#include <iomanip>
#include <stdlib.h>
#include <string.h>
constexpr unsigned int MAX_USER = 100;
constexpr unsigned int LENGTH_UNIT = 30;
constexpr unsigned int LENGTH_NAME = 50;
constexpr unsigned int LENGTH_MAIL = 80;
constexpr unsigned int MAX_NUM = 35;
constexpr unsigned int LENGTH_IC = 50;
struct USER {
char unit_number[LENGTH_UNIT];
char name[LENGTH_NAME];
char email[LENGTH_MAIL];
char phone[MAX_NUM];
char IC[LENGTH_IC];
};
void add_new_user(void)
{
std::ifstream in_file("r:\\information.txt");
if (!in_file)
{
std::cerr << "\n\n*** Error opening source txt file\n\n";
}
else
{
static USER U_list[MAX_USER]; // Should not be put onto stack of function
int index = 0; // Should be unsigned
while (in_file)
{
in_file.getline(U_list[index].unit_number, LENGTH_UNIT); // Should be optimized
in_file.getline(U_list[index].name, LENGTH_NAME);
in_file.getline(U_list[index].email, LENGTH_MAIL);
in_file.getline(U_list[index].phone, MAX_NUM);
in_file.getline(U_list[index].IC, LENGTH_IC);
++index;
}
in_file.close();
bool in = false;
do
{
std::cout << "==================================================\n"
<< "==\tUser Management System\t\t==\n"
<< "==================================================\n"
<< "==\tAdd New User\t\t\t==\n"
<< "==================================================\n";
std::cout << "\nEnter unit number of the new user:\n";
USER new_user{};
std::cin.getline(new_user.unit_number, LENGTH_UNIT);
// We want to count, how many of these units are already in
unsigned int unitCounter = 0;
// Search the unit number that was entered by the user in the list of existing users read from the file
for (int i = 0; i < index; i++) {
if (std::strcmp(new_user.unit_number, U_list[i].unit_number) == 0) {
++unitCounter;
}
}
// If this units exists
if (unitCounter == 1)
{
std::cout << "\nEnter new user's name:\n";
std::cin.getline(new_user.name, LENGTH_NAME);
std::cout << "\nEnter new user's EMAIL ADDRESS:\n";
std::cin.getline(new_user.email, LENGTH_MAIL);
std::cout << "\nEnter new user's phone number:\n";
std::cin.getline(new_user.phone, MAX_NUM);
std::cout << "\nEnter new user's IC:\n";
std::cin.getline(new_user.IC, LENGTH_IC);
char option{};
do
{
std::cout << "\nDo you confirm to register this new user? [Yes - Y, No - N]\n=>";
std::cin >> option;
if (option != 'Y' && option != 'N' && option != 'y' && option != 'n')
{
std::cout << "\nWrong input. Enter again.\n\n";
}
} while (option != 'Y' && option != 'N' && option != 'y' && option != 'n');
if (option == 'Y' || option == 'y')
{
std::ofstream out_file("r:\\information.txt", std::ios::app);
out_file << std::left << new_user.unit_number << '\n';
out_file << new_user.name << '\n';
out_file << new_user.email << '\n';
out_file << new_user.phone << '\n';
out_file << new_user.IC << std::endl;
out_file.close();
std::cout << "\nThe new user is successfully registered.\n\n";
in = true;
}
else if (option == 'N' || option == 'n')
{
std::cout << "\nThe new user is not registered.\n\n";
in = true;
}
}
else if (unitCounter == 0) {
std::cout << "\n\nError Unit does not exist\n\n";
in = true;
}
else if (unitCounter >= 1) { // For 2 and more
std::cout << "\n\nError. Max 2 residents per unit\n\n";
in = true;
}
} while (!in);
}
}
int main() {
add_new_user();
}
Now, add a little bit minimum C++. Still keep the nasty char arrays instead of strings.
But, this solution will work already.
#include <iostream>
#include <fstream>
#include <iomanip>
#include <stdlib.h>
#include <string.h>
constexpr unsigned int MAX_USER = 100;
constexpr unsigned int LENGTH_UNIT = 30;
constexpr unsigned int LENGTH_NAME = 50;
constexpr unsigned int LENGTH_MAIL = 80;
constexpr unsigned int MAX_NUM = 35;
constexpr unsigned int LENGTH_IC = 50;
struct USER {
char unit_number[LENGTH_UNIT];
char name[LENGTH_NAME];
char email[LENGTH_MAIL];
char phone[MAX_NUM];
char IC[LENGTH_IC];
friend std::istream& operator >> (std::istream& is, USER& u) {
is.getline(u.unit_number, LENGTH_UNIT);
is.getline(u.name, LENGTH_NAME);
is.getline(u.email, LENGTH_MAIL);
is.getline(u.phone, MAX_NUM);
return is.getline(u.IC, LENGTH_IC);
}
friend std::ostream& operator << (std::ostream& os, const USER& u) {
return os << u.unit_number << '\n' << u.name << '\n' << u.email << '\n' << u.phone << '\n' << u.IC << std::endl;
}
};
struct DataBase {
USER U_list[MAX_USER]{};
unsigned int numberOfUserInDatabase{};
friend std::istream& operator >> (std::istream& is, DataBase& d) {
while (is >> d.U_list[d.numberOfUserInDatabase])
++d.numberOfUserInDatabase;
return is;
}
};
DataBase dataBase;
void add_new_user(void)
{
std::ifstream in_file("r:\\information.txt");
if (!in_file)
{
std::cerr << "\n\n*** Error opening source txt file\n\n";
}
else
{
in_file >> dataBase;
in_file.close();
bool in = false;
do
{
std::cout << "==================================================\n"
<< "==\tUser Management System\t\t==\n"
<< "==================================================\n"
<< "==\tAdd New User\t\t\t==\n"
<< "==================================================\n";
std::cout << "\nEnter unit number of the new user:\n";
USER new_user{};
std::cin.getline(new_user.unit_number, LENGTH_UNIT);
// We want to count, how many of these units are already in
unsigned int unitCounter = 0;
// Search the unit number that was entered by the user in the list of existing users read from the file
for (unsigned int i = 0; i < dataBase.numberOfUserInDatabase; i++) {
if (std::strcmp(new_user.unit_number, dataBase.U_list[i].unit_number) == 0) {
++unitCounter;
}
}
// If this units exists
if (unitCounter == 1)
{
std::cout << "\nEnter new user's name:\n";
std::cin.getline(new_user.name, LENGTH_NAME);
std::cout << "\nEnter new user's EMAIL ADDRESS:\n";
std::cin.getline(new_user.email, LENGTH_MAIL);
std::cout << "\nEnter new user's phone number:\n";
std::cin.getline(new_user.phone, MAX_NUM);
std::cout << "\nEnter new user's IC:\n";
std::cin.getline(new_user.IC, LENGTH_IC);
char option{};
do
{
std::cout << "\nDo you confirm to register this new user? [Yes - Y, No - N]\n=>";
std::cin >> option;
if (option != 'Y' && option != 'N' && option != 'y' && option != 'n')
{
std::cout << "\nWrong input. Enter again.\n\n";
}
} while (option != 'Y' && option != 'N' && option != 'y' && option != 'n');
if (option == 'Y' || option == 'y')
{
std::ofstream out_file("r:\\information.txt", std::ios::app);
out_file << new_user;
out_file.close();
std::cout << "\nThe new user is successfully registered.\n\n";
in = true;
}
else if (option == 'N' || option == 'n')
{
std::cout << "\nThe new user is not registered.\n\n";
in = true;
}
}
else if (unitCounter == 0) {
std::cout << "\n\nError Unit does not exist\n\n";
in = true;
}
else if (unitCounter >= 1) { // For 2 and more
std::cout << "\n\nError. Not more than max 2 residents per unit allowed\n\n";
in = true;
}
} while (!in);
}
}
int main() {
add_new_user();
}
And last but not least: Remove all C code. And make it an advanced C++ solution.
This will be too complicated in the beginning, but can show the way to go:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
// Here all unit/user data will be stored
const std::string userDataBaseFileName{ "information.txt"};
// CLass for one user
class User {
// User data
std::string unit{};
std::string name{};
std::string email{};
std::string phoneNumber{};
std::string IC{};
public:
// IO functions. Extractor operator
friend std::istream& operator >> (std::istream& is, User& u) {
std::getline(is, u.unit);
std::getline(is, u.name);
std::getline(is, u.email);
std::getline(is, u.phoneNumber);
return std::getline(is, u.IC);
}
// Inserter operator
friend std::ostream& operator << (std::ostream& os, const User& u) {
return os << u.unit << '\n' << u.name << '\n' << u.email << '\n' << u.phoneNumber << '\n' << u.IC << std::endl;
}
// Check, if 2 units are equal
bool equalUnit(const User& other) const { return unit == other.unit; }
};
// IO functions. Extractor operator
class DataBase {
std::vector<User> user{};
public:
// Here is the number of maximum users per unit
static constexpr size_t MaxUserPerUnit{ 2u };
static_assert(MaxUserPerUnit >= 1 , "Error: At least one user must be allowed per Unit\n"); // Must be greater than 1
// Calculate number of user per unit
size_t usersInUnit(const User& testUser) const { return std::count_if(user.begin(), user.end(), [&](const User& u) { return u.equalUnit(testUser); }); }
// IO functions. Extractor operator. Read all users
friend std::istream& operator >> (std::istream& is, DataBase& d) {
User tempUser{};
while (is >> tempUser) d += tempUser;
return is;
}
// Inserter operator
friend std::ostream& operator << (std::ostream& os, const DataBase& u) {
std::copy(u.user.begin(), u.user.end(), std::ostream_iterator<User>(os));
return os;
}
// Ad a new use to the database via += operator
DataBase& operator+=(const User& u) {
if (usersInUnit(u) < MaxUserPerUnit)
user.push_back(u);
else
std::cerr << "Error. Too many users in unit. Ignoring user:\n" << u << "\n\n";
return *this;
}
};
DataBase dataBase;
void addNewUser()
{
// Open source file and chek, if it could be opened
if (std::ifstream informationDataFileStream{ userDataBaseFileName }; informationDataFileStream) {
// The file is open. Read all data from file
informationDataFileStream >> dataBase;
// Some debug output
std::cout << "\n\nThe following data has been read from file:\n\n" << dataBase << "\n\n";
// Ask operator to enter a new user
std::cout << "\nPlease enter new user information. One at a line. In this order: Unit, Name, Email, Phone Number, ID:\n";
// Read a new user from operator
User tempUser{}; std::cin >> tempUser;
// Confirm, if the user wants really add this info.
std::cout << "\n\nYour entered the following data:\n\n" << tempUser << "\n\nDo you really want to add? (y = yes, everything else = No\n--> ";
char option{}; std::cin >> option;
if (option == 'y') {
// Give confirmation to user
std::cout << "\n\nData will be added to database\n";
// add new user to database
dataBase += tempUser;
// Save all data to disk
if (std::ofstream dataFileStream{ userDataBaseFileName }; dataFileStream) {
dataFileStream << dataBase;
}
else std::cerr << "\nError: Could not open file '" << userDataBaseFileName << "' for writing\n\n";
}
else std::cout << "\n\nData will NOT be added to database\n";
}
else std::cerr << "\nError: Could not open file '" << userDataBaseFileName << "' for reading\n\n";
}
int main() {
addNewUser();
}
Have fun . . .