Find a string in a input file - C++ - 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!

Related

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.

How Do I read and Output the Contents of a File and the Number of Words it Contains?

I am attempting to write a program for homework which reads the contents of a notepad file and displays the contents and the number of words int he file. My code currently outputs nothing when I enter the name of the names of files I am using to test the program, and the input validation while loop I inserted does not function either.
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
//Declare needed variables
string fileName, contents;
int wordCount = 0;
ifstream inData;
//Display program info
cout << "*** A SIMPLE FILE PROCESSING PROGRAM ***" << endl;
//Prompt user input
cout << "Enter a filename or type quit to exit: ";
cin >> fileName;
inData.open(fileName.c_str());
//Inform the user when their input is invalid and ask them to input another
file name
while (!inData)
{
inData.clear();
inData.ignore(200, '\n');
cout << "File not found. Please type a correct file name." << endl;
cin >> fileName;
inData.open(fileName.c_str());
}
inData >> contents;
//Read and output the contents of the selected file
while (inData)
{
cout << fileName << " data\n";
cout << "***********************" << endl;
inData >> contents;
wordCount++;
cout << contents << endl;
inData >> contents;
}
//Display the number of words in the file
cout << "***********************" << endl;
cout << fileName << " has " << wordCount << " words." << endl;
inData.close();
return 0;
}
The code compiles in its current state [but does not produce the desired outcome.
I will show you one of the many possible solutions.
But I would not recomend, to check the validity of a filename in a loop. You will give the user no chance to escape. Hence, I propose to open the file, and, if that does not work, show an error message and quit.
Then, what sounds easy in the beginning like, count the words, is not really that easy. What is a word? Characters only, or characters mixed with digits or even an underscore in it like for C++ variable names? Needs to be defined.
Additionally you may have separators like commas or one and more other white spaces. So a line like "Hello,,,,World" cannot be so easily counted. If you try to read the 2 words, then you will see a surprise.
std::string s1{};
std::string s2{};
std::istringstream iss("Hello,,,,World");
iss >> s1 >> s2;
Will read everything in s1!
The solution is that we define clearly what a word is. And this we will do with a std::regex. In the below example we use characters, digits and _
Then we use the regex_iterator to find all occurences of the regex (the word) in the line. We substract the end from the beginning with std::distance, which will give us the count of the words.
Then we give an output to the user in whatever format.
It may seem complicated. But it is precise. And rather flexible. Try to anaylze line by line and you will understand it.
Please see:
#include <iostream>
#include <string>
#include <regex>
#include <fstream>
#include <iomanip>
int main()
{
// Get a filename from the user
std::cout << "Enter a filename:\n";
std::string filename{}; std::cin >> filename;
// Try to open and read the file
std::ifstream fileStream(filename);
if (fileStream) {
// We will count all words
size_t numberOfWordsOverall{ 0 };
// We will also count the lines in the file
size_t lineCounter{ 1 };
// Define, what a word is. In this case: Characters, Digits and _
std::regex regexForWord("[\\w\\d_]+");
// Read all lines in file
std::string line{};
while (std::getline(fileStream, line)) {
// Count the numbers of words in one line
const size_t numberOfWordsInLine = std::distance(
std::sregex_token_iterator(line.begin(), line.end(), regexForWord, 1),
std::sregex_token_iterator()
);
// Update the overall word counter
numberOfWordsOverall += numberOfWordsInLine;
// Show result to user
std::cout << "# " << std::left << std::setw(2) << lineCounter++ << " (Words in line: "<< std::setw(2) << numberOfWordsInLine <<
" Words overall: " << std::setw(4) << numberOfWordsOverall << ") Line content --> " << line << '\n';
}
}
else {
std::cerr << "Could not open file '" << filename << "'\n";
}
return 0;
}
Hope this helps . . .

C++ file readin with delimiters

Name~Proper Name~HR number~HD number~distance- format of the data in file
include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
string name;
string properName;
string hrN;
string hdN;
double distance;
ifstream fin;
string fileName;
cout << "Enter a file name: ";
getline(cin, fileName);
fin.open(fileName);
if(fin.fail())
{
cout << "Unable to open the file" << endl;
return -1;
}
else
{
string name;
string properName;
string hrN;
string hdN;
double distance;
string line;
string pN1;
bool found = false;
while(true)
{
getline(fin, name, '~');
getline(fin, properName,'~');
getline(fin, hrN,'~');
getline(fin, hdN, '~');
fin >> distance;
cout << "Enter a proper star name:";
string pN1;
cin >> pN1;
if(pN1.compare(properName))
{
found = true;
cout << "Star : "<< "proper name: "<<pN1<< "distance: "<<distance<<"light years" << "HD num: "<<hdN << "HR num: "<< hrN << "common name: "<< name << endl;
}
}
if (found == false)
{
cout << "No star with the properName"<<" "<< pN1 <<" "<<"was found"<< endl;
}
fin.close();
}
return 0;
}
This is what Ive got so far.
I'm just not sure how to store the variables, in order to search for a variable and display the contents of a specific line to the screen
There's quite a lot wrong with your code. You've duplicated the variable names for some reason, your input loop never terminates and you've asked the question about the star name inside the input loop. I'd say you're rushing into coding without thinking about what you are doing or knowing where you are going.
Lets take it one piece at a time.
The first thing you have is a bunch of related data about stars, name, proper name, distance etc. So lets express that fact by grouping the related data in a struct
struct StarData
{
string name;
string properName;
string hrN;
string hdN;
double distance;
};
Now lets write a loop that reads that data and importantly finishes when there is no more data
StarData data;
// loop until no more data
while (getline(fin, data.name, '~') && getline(fin, data.properName, '~') &&
getline(fin, data.hrN, '~') && getline(fin, data.hdN, '~') &&
(fin >> data.distance))
{
}
So this loop reads star data, one star at a time into the data variable. But as yet doesn't do anything with that data.
There are various ways to store the data so that it will be searchable, it's a very common thing to want to do, so C++ does most of the work for you. I'm going to use something called a std::map which is a data structure which is very good at storing data so it can be searched. Let modify the loop above
#include <map>
std::map<string, StarData> star_database;
StarData data;
// loop until no more data
while (getline(fin, data.name, '~') && getline(fin, data.properName, '~') &&
getline(fin, data.hrN, '~') && getline(fin, data.hdN, '~') &&
(fin >> data.distance))
{
// add star data to database using proper name as the key
star_database[data.properName] = data;
}
This code stores the star data in a variable called star_database. In particular because we keyed the map on the proper name, i.e. because we said star_database[data.properName] = data; later we'll be able to use the proper name to look up the data about the star.
Now we've done the while loop, lets ask the question about the star to look up, remember this happens after the while loop above, not inside it.
cout << "Enter a proper star name:";
string pN1;
cin >> pN1;
Now we'll do the lookup
auto answer = star_database.find(pN1); // lookup the proper name in the database
if (answer == star_database.end()) // did we find it?
{
cout << "No star with the proper name "<< pN1 <<" was found"<< endl;
}
else
{
cout << "Star : "<< "proper name: "<< pN1 <<
" distance: "<< answer->second.distance << " light years" <<
" HD num: "<< answer->second.hdN <<
" HR num: "<< answer->second.hrN <<
" common name: "<< answer->second.name << endl;
}
I've skipped over quite a lot of detail (I can only explain so much) but hopefully you've got the basic idea and can research the rest yourself.
All code untested, apologies in advance for any typos.

More appropriate way to loop input from a file?

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.

Why can't I write to a file if it is located within an if statement?

I am trying to create a very simple program that writes to a file, but can't understand why it won't let me write to a file if I put it within an if statement! Here's the code:
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
#include <stdlib.h>
using namespace std;
void readFile();
int main()
{
string line;
string today;
string readOrWrite;
cout << "Do you want to write to a file or read a file? " << endl;
cout << "Type \"write\" or \"read\" ";
cin >> readOrWrite;
if (readOrWrite == "read")
{
readFile();
}
else if (readOrWrite == "write")
{
cout << "How are you today? " << endl;
getline(cin, today);
ofstream myJournal;
myJournal.open("Journal.txt", ios::app);
myJournal << today << " ";
myJournal.close();
}
else
{
return 0;
}
return 0;
}
void readFile()
{
ifstream myJournal;
myJournal.open("Journal.txt");
string line;
if (myJournal.is_open())
{
while (getline(myJournal, line))
{
cout << line << endl;
}
myJournal.close();
}
else
{
cerr << "Error opening file ";
exit(1);
}
}
When I move it out of the if statement, it works smoothly and is able to write to the file, but when I place it inside, it opens the program, asks me the "Do you want to write to a file or read a file? ", I type "write", then it says "How are you today? " and then ends the program, printing "Press any key to continue...". Any help?
it says "How are you today? " and then ends the program, printing "Press any key to continue...". Any help?
std::istream::ignore should help in that case you are encountering.
cout << "How are you today? " << endl;
cin.ignore(10, '\n'); // Inserted
getline(cin, today);
Why do we need that in between?
It takes out 10 characters, which is enough amount of length, from the buffer and stops if it encounters a newline, which is '\n'. (Remember that you press the key 'enter' after typing "wrtie")
By doing so you can move on to the next new line, preventing std::cin from any parse failure.
More info : http://www.cplusplus.com/reference/istream/istream/ignore/