More appropriate way to loop input from a file? - c++

I want to be able to loop this file opening and closing to continually search for names.
The first time is no problem and output is what is expected then, when choosing y for yes, an output loop occurs.
Any ideas as to why this would happen? The logic seems more than correct.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
string boys, girls, name;
int rank;
char end = 'n';
while (end != 'y' || end != 'Y')
{
cout << "Enter a name to search";
cin >> name;
ifstream input;
input.open("Names2016");
if (input.fail())
cout << "Failed to open file.\n";
while (!input.eof())
{
input >> rank >> boys >> girls;
if (boys == name)
cout << name << " ranks " << rank << " among boys.\n";
if (girls == name)
cout << name << " ranks " << rank << " among girls.\n";
}
input.close();
cout << "Would you like to search another name?\n"
<< "Enter Y for yes or N for no.\n";
cin >> end;
}
return 0;
}

There are a some of things you can do to make this code better,
The first is to use ifstreams and do file input/output the proper idiomatic way in a loop, don't use .eof() to check for end of file in a loop condition (the answer linked in the comments is a good place to start if you want to know why),
The second thing you want to check for validity of the file with a simple if (!file) its much cleaner IMO.
The third thing is, when you have a local file handle like you do in your code, then you can just let it go out of scope and let the destructor cleanup the file and close() it, it's the C++ RAII way of doing things (notice that I have removed the open() method to the constructor call (which does the same thing)
Use cerr instead of cout to report errors
Use char instead of int to represent characters
Not a big change, but using std::toupper like advised in the other answer's comments is a good readable way to check for uppercase and lowercase values at the same time
#include <iostream>
#include <fstream>
#include <string>
#include <cctype>
using namespace std;
int main()
{
string boys, girls, name;
int rank;
char end = 'n';
while (std::toupper(end) == 'Y')
{
cout << "Enter a name to search";
cin >> name;
ifstream input{"Names2016"};
// change here
if (!input) {
cerr << "Failed to open file.\n";
}
while (input >> rank >> boys >> girls)
{
if (boys == name)
cout << name << " ranks " << rank << " among boys.\n";
if (girls == name)
cout << name << " ranks " << rank << " among girls.\n";
}
// change below, just let the file handle go out of scope and close
// input.close();
cout << "Would you like to search another name?\n"
<< "Enter Y for yes or N for no.\n";
cin >> end;
}
return 0;
}
But you can do better on the I/O if your file isn't guaranteed to change over different iterations (in which case you probably need to make sure that there is no race anyway, so I am assuming the file does not change much). Read in the file once and save that information to be used later
#include <iostream>
#include <fstream>
#include <string>
#include <cctype>
#include <unordered_map>
#include <vector>
using namespace std;
int main()
{
string boys_name, girls_name, name;
int rank;
char end = 'n';
ifstream input{"Names2016"};
if (!input) {
cerr << "Failed to open file" << endl;
}
// preprocess the information and store it in a map
// making a map from string to vector because it is unclear whether
// there is a 1-1 mapping from the name to the rank for each name
unordered_map<string, vector<int>> boys;
unordered_map<string, vector<int>> girls;
while (input >> rank >> boys_name >> girls_name) {
boys[boys_name].push_back(rank);
girls[girls_name].push_back(rank);
}
while (std::toupper(end) == 'Y')
{
cout << "Enter a name to search";
cin >> name;
// use the map to do the lookup, much faster than reading
// the entire file over and over again
}
return 0;
}

First of all, what is this supposed to mean int end = 'n'; Are you assigning an integer with a character value?!
And why are you opening the same file inside the loop. You should probably open it only once at the beginning of the program.
And the eof doesn't have what to check for, because you have to read from a file to reach its end.

Related

Is it possible to put multiple names in one string?

I just started learning C++ and I'm currently following a tutorial on YouTube.
I thought it was fun to make a very simple 'access' program. If I type in my name it says, "Welcome!" If I type in another name it says, "access denied". It worked perfectly fine, but then I wanted the program to say "Welcome!" to two different names. So, I wanted to add a second name in the string, but I couldn't figure out how to do that. I googled a lot but I couldn't find anything. In the end, I came to string name = ("Joe", "Sean");, but here, it was only valid for Sean. I just can't figure out how to put multiple names in one string and make them both work. I hope you can help me, here is my code:
#include <iostream>
#include <cmath>
#include <string>
using namespace std;
int main()
{
string name = ("Joe", "Sean");
string input;
cout << "What is your name?\nMy name is: ";
cin >> input;
if(input == name){
cout << "Welcome, "<< input <<"! ";
} else {
cout << "Access denied";
}
return 0;
}
This is a way to do it using a vector of strings, so you can adapt easily with more names :
#include <iostream>
#include <vector>
using namespace std;
void printMessage(string message)
{
std::cout << message << std::endl;
}
int main()
{
vector<string> names{"Joe", "Sean", "Paul"};
string input;
cout << "What is your name? " << endl;
cin >> input;
for (string name : names)
{
if (name == input)
{
printMessage("Welcome!");
return 0;
}
}
printMessage("Access Denied!");
return 0;
}
The problem is in the string variable "name". You need an array of strings, not a single string.
This is an example implementation:
#include <iostream>
#include <cmath>
#include <string>
using namespace std;
int main()
{
string names[] = {"Joe", "Sean"};
string input;
cout << "What is your name?\nMy name is: ";
cin >> input;
for (int i = 0; i < end(names) - begin(names); i++) {
if(input == names[i]){
cout << "Welcome, "<< input <<"! " << endl;
return 0;
}
}
cout << "Access denied" << endl;
return 0;
}
You encountered some quirky features of C++ in the approach you are using to initialize your string variable:
string s1 = ("Joe"); // creates a string "Joe"
string s2 = ("Joe", "Sean"); // creates 2 strings, "Joe" and "Sean", and the variable s2 stores only the latter!
For more details on the different methods for initializing variables there has been an interesting discussion in this previous question.

c++: Expected primary-expression

I have been struggling with a certain error that doesn't make sense to me. Whenever I try to compile this program, it tells me that I'm missing a semicolon when I am not.
It seems the error is linked to a specific block of code, that being the if statement that checks stock. Since I know c++ can be platform specific, I'm running debian 9 and the atom ide if that's any help.
Here is the specifc error:
error: expected primary-expression before ',' token
getline(string,line);//gets string`
and the code:
#include <iostream>
#include <fstream>
using namespace std;
int main ()
{
cout << "store stocking system: \n"; // yadda yadda yadda UX
cout << "commands: \n";
cout << " help: shows available commands\n check stock: checks store stock\n enter stock: enter new stock items\n";
cout << " exit: terminates the program\n clean house: deletes all stock\n";
home: // main loop in program
string output;
output = ">> ";
cout << output;
string stock;
string item; // this whole block just defines things and gets input
int itemNumber;
string userInput;
getline(cin,userInput);
if (userInput == "exit")
{
return 0;
}
if (userInput == "enter stock")
{ // enters new stock
cout << "enter item\n>> "; //item name
cin >> item;
cout << "enter amount\n>> "; //amount of item
cin >> itemNumber;
ofstream myfile; //makes file
myfile.open("stock.txt"); //opens myfile
myfile << "\n" << item << "," << itemNumber << "\n"; //writes to file
myfile.close();// closes file
cout << "done";
goto home; //finishes and goes to main loop
}
if (userInput == "check stock") // where the problem is
{
string line;
ifstream file("stock.txt");//checks fo file
file.open("stock.txt");//opens file
getline(string,line);//gets string
file.close();//closes it
cout << line << "\n";
goto home;
}
if (userInput == ""){
goto home;
}
else
{
cout << "\033[1;31mplease use a proper command:\033[0m\n";
goto home;
}
return 0;
}
Are you missing this by any chance?
#include <string>
I believe it simply needs to be getline(file,line) rather than getline(string,line) and then you should be sorted.
string is recognized as a type name, of type std::string which you generously exposed by line using namespace std;. This particular error message is caused by fact that string isn't an expression which can be evaluated . In context of your code it should be
getline(file,line);
PS. Standard would say that you have to include <string> header to use component std::string. Your code compiles thanks to an implementation-defined feature, was imported with <iostream> in this version of header.

Keep getting "member reference base type 'string [1000]' is not a structure or union" error and dont know how to fix it?

This program reads an SSN from the user and checks if it matches an SSN in a text file provided. Tried switching the array to a vector and it didnt work. Tried putting the array in the structure function and using info:: but nothing seems to work. I know this is pretty basic but I cant get it, thanks.
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char* argv[]){
struct info{
string SSN;
string firstName;
string lastName;
};
string list[1000];
string userSSN;
char x;
fstream input(argv[1]);
int i = 0;
while(!input.eof()){
input >> list.x[i] >> list.SSN[i] >> list.firstName[i] >> list.lastName[i];
i++;
}
input.close();
cout << "Input a SSN:" << endl;
cin >> userSSN >> endl;
for(int k = 0; k < i; k++){
if(userSSN.compare(list.SSN[k]) == 0){
cout << "Found at location " << k << endl;
}
}
}
list is just an array of 1000 std::strings. It looks like you need it to be of type info. Even that won't solve your problems as info has no member named x. After that, to access a member of info in an array would be like
list[i].SSN
not
list.SSN[i]
There are so many things wrong with this code that I don't know where to start. Let's see:
Using using namespace std; is almost always a bad idea, and if you have an identifier list, then it's even worse because it conflicts with std::list.
Using std::string without #include <string> is not guaranteed to work. It may work if you include some other standard header, but don't rely on it.
Your variable x is unused.
string list[1000]; should probably be info list[1000];.
list.SSN[i] et al should probably be changed to list[i].SSN.
list.x[i] does not make sense at all and can probably be removed. (Or you meant to read into the otherwise unused x to skip parts of the file.)
You cannot read from std::cin into std::endl. Remove std::endl from that line.
Using std::string's compare function is pretty strange here, just use ==.
The issues in your code were already pointed out in other's answers. I'll show you a "working" example:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using std::string;
using std::vector;
using std::cout;
using std::cin;
struct info {
char x; // You were reading that from the file so I added it
string SSN;
string firstName;
string lastName;
friend std::istream &operator>>( std::istream &is, info &i ) {
// maybe you should check data input somehow...
is >> i.x >> i.SSN >> i.firstName >> i.lastName;
return is;
}
};
int main(int argc, char* argv[]) {
// check if a file name has been passed as a parameter
string file_name;
if ( argc < 2 ) {
cout << "Please, enter the input file name:\n";
cin >> file_name;
}
else
file_name = argv[1];
std::ifstream input(file_name);
if ( !input ) {
std::cerr << "Error. Unable to open file: " << file_name << '\n';
exit(-1);
}
// Just use a vector to store all the structs
vector<info> my_list;
info temp_info;
while( input >> temp_info ) {
my_list.push_back(temp_info);
}
input.close();
cout << "Input a SSN: ";
string userSSN;
cin >> userSSN;
// That's not cheap. You may want to change the container or sort it
for ( int k = 0; k < my_list.size(); k++ ) {
if ( userSSN == my_list[k].SSN ) {
cout << "Found at location " << k << '\n';
}
}
return 0;
}

Find a string in a input file - C++

I need to find a string (link name) input by the user in a text file.
How can approach a solution in c++? Do I have to store the file context in structs in order to read the data later? Or can I just open and read the file whenever i want to look for info?
Thank you!
Input file sample
111.176.4.191 www.yahoo.com 01/04/2013
111.176.4.191 www.yahoo.com 01/09/2013
192.168.1.101 www.yahoo.com 01/04/2013
111.176.4.191 www.yahoo.com 01/12/2013
192.168.1.101 www.espn.com 01/05/2013
C++ code
#include <fstream>
#include <iostream>
#include <string>
#include <cstdlib>
#include <string>
using namespace std;
//gobal variables, procedures
void fileinfo1(string);
char IP_Address [12];
char Link_Name [50];
char Date_Accessed [8];
string filename;
int menu;
int main()
{
// the user will input the file name here
cout << "Enter filename> ";
getline( cin, filename );
fstream file( filename.c_str() );
if (!file)
{
cout << "Invalid file.\n";
return EXIT_FAILURE;
}
// the program will display the file context
else
{
string line;
int count = 10;
while ((count > 0) && getline( file, line ))
{
cout << line << '\n';
count--;
}
file.close();
}
return EXIT_SUCCESS;
// The user will be able to choose to see info about all entries or a particular one
cout << "Please select a menu option:";
cout << "1)Link Information in date range";
cout << "2)Information about all links";
cout << "3)Quit the program";
cin >> menu;
switch (menu) {
// see info about a particular link
case 1: fileinfo1(filename);
break;
case 2:
break;
case 3:
break;
default: cout << "Please a choose a number between 1 and 3";
break;
}
// the file is passed to this function
void fileinfo1(string filename) {
//the user will input a link e.g www.espn.com
cout << "What is the link name? ";
cin >> Link_Name;
// and also input date range (start-end)
cout << "What is the starting date? " ;
cin >> Date_Accessed;
cout << "What is the ending date? " ;
cin >> Date_Accessed;
// Now, here's where I'm having trouble
// I need to find the wwww.espn.com in my file based on the range date , so that i will be able to increment the number of hits
unsigned int curLine = 0;
while (getline(filename, line)) { // I changed this, see below
curLine++;
if (line.find(search, 0) != string::npos) {
cout << "found: " << search << "line: " << curLine << endl;
}
}
}
}
Thank you!
This Part of the code shouldnt be written into your main() function.
// the file is passed to this function
void fileinfo1(string filename) {
//the user will input a link e.g www.espn.com
cout << "What is the link name? ";
cin >> Link_Name;
// and also input date range (start-end)
cout << "What is the starting date? " ;
cin >> Date_Accessed;
cout << "What is the ending date? " ;
cin >> Date_Accessed;
// Now, here's where I'm having trouble
// I need to find the wwww.espn.com in my file based on the range date , so that i will be able to increment the number of hits
unsigned int curLine = 0;
while (getline(filename, line)) { // I changed this, see below
curLine++;
if (line.find(search, 0) != string::npos) {
cout << "found: " << search << "line: " << curLine << endl;
}
}
}
and you are using way to many global variables which are really not necessary. And you dident declare the variables line and search. This shoudnt even compile.
Do you want a quick and dirty solution or an elegant one?
For an elegant solution, I would:
Ditch the globals.
Read the entire file into memory before parsing it.
Generate an internal database for your data.
Write a few query functions that return a subset of your data.
For your particular case, you could use a std::multimap < LinkName, DateAndIP > to find all data relating to the link. DateAndIP could be a typedef to std::multimap < Date, IP > . If you've never used multimap, this will be a good learning experience. Write your compare functions and use the find member function to return only what you're looking for.
Good luck and happy coding!

How to print specific array entries from a .txt file in C++?

First, I'm very new to coding in C++.
So, I have a .txt file, with names and numbers--here's an example.
chris 5
tara 7
Sam 13
Joey 15
I would like to use this code to retrieve the names and numbers, but how does one print specific array entries instead of just the variables name and number (I want it to show the name and the number on the screen)?
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main() {
string name;
int number;
struct sEntry
{
std::string name;
int number;
};
sEntry entries[256];
std::ifstream fin("input.txt"); // opens the text file
int nb_entries; // Keeps track of the number of entries read.
for (nb_entries = 0; fin.good() && nb_entries < 256; nb_entries++) // Keep going until we hit the end of the file:
{
fin >> entries[nb_entries].name;
fin >> entries[nb_entries].number;
cout << "Here, "<< name <<" is name.\n";
cout << "Here, "<< number <<" is number.\n";
}
}
You're writing out name and number, but those aren't the variables you've read. You've read array entries.
Getting it working as simply as possible just comes down to changing your cout lines to be:
cout << "Here, " << entries[nb_entries].name << " is name.\n";
cout << "Here, " << entries[nb_entries].number << " is number.\n";
No need for a std::vector, ther's nothing wrong with how you've done it.
Instead of using a plain C array of sEntry you should use a C++ vector instead (which can change size dynamically). Then you create a new sEntry instance inside your loop (which can just use fin.eof() as termination condition then) and use the operator>>() to assign the values. Afterwards you use push_back() to add the sEntry instances to your vector.
You need to use the sEntry.name, sEntry.number fields for output on the screen, name and number as shown in your code won't ever receive values.
#include <vector>
#include <string>
#include <iostream>
struct sEntry
{
std::string name;
int number;
};
int main() {
string name;
int number;
std::vector<sEntry> entries;
std::ifstream fin("input.txt"); // opens the text file
// int nb_entries; // Keeps track of the number of entries read. -> not necessary, use entries.size()
while(!fin.eof()) // Keep going until we hit the end of the file:
{
sEntry entry;
fin >> entry.name;
fin >> entry.number;
cout << "Here, "<< entry.name <<" is name.\n";
cout << "Here, "<< entry.number <<" is number.\n";
entries.push_back(entry);
}
}