C++ file readin with delimiters - c++

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.

Related

Reading file data to structure

For a homework assignment, I am supposed to create a program that uses structures to hold an artists' discography information; i.e. name, start year, end year, genre(this can be more than one) and albums with their release year(which I interpreted to mean a second structure). All this information should be read from a file.
I'm having a little bit of trouble reading data from the file into the structure variables. My main issue is that a couple of my struct variables can hold multiple strings and I am confused as to how to read them into the variables as separate values. I've tried using 2D arrays, but that doesn't seem to work, so I think that vectors might be the way to go, but that seems to be throwing up issues as well. The file is formatted as such:
artist name
artist genres;....;...
start year
end year
number of albums
album name, album year; ..., ...;
So far this is the code that I have written:
#include <fstream>
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
struct Discography {
vector<string> trackTitle;
vector<int> trackYear;
};
struct Artist {
string artistName;
vector<string> genres;
int startingYear;
int endingYear;
int numOfTracks;
struct Discography disc;
};
istream& operator>>(istream& input, Artist& artist)
{
input >> artist.artistName;
//input >> artist.genres;
input >> artist.startingYear;
input >> artist.endingYear;
input >> artist.numOfTracks;
//input >> artist.disc.trackTitle;
//input >> artist.disc.trackYear;
return input;
}
void displayFileContents();
void searchByName();
void searchByGenre();
void searchBySong();
void searchByYear();
int main()
{
/*integer variable for menu selection*/
int selection;
vector<Artist> art;
do
{
cout << "\nPlease make a selection from one of"
<< "\nthe six options below.\n";
cout << "\n1. Display All Information\n";
cout << "2. Search by artist name\n";
cout << "3. Search by genre\n";
cout << "4. Search by song title\n";
cout << "5. Search by an active year\n";
cout << "6. Exit Program\n";
cout << "\nEnter your selection: ";
/*reading user menu selection*/
cin >> selection;
if (selection == 1) {
displayFileContents();
}
else if (selection == 2) {
searchByName();
}
else if (selection == 3) {
searchByGenre();
}
else if (selection == 4) {
searchBySong();
}
else if (selection == 5) {
searchByYear();
}
else if (selection == 6) {
cout << "Good Bye!";
}
else {
cout << "You did not enter a valid selection\n\n";
}
} while (selection != 6);
return 0;
}
void displayFileContents() {
ifstream artistFile;
artistFile.open("My Artists.txt");
Artist a[5];
if (!artistFile.is_open()) {
cout << "File could not open";
}
else
while (std::getline(artistFile, a->artistName)) {
std::cout << a->artistName << "\n";
}
}
void searchByName() {
}
void searchByGenre() {
}
void searchBySong() {
}
void searchByYear() {
}
The ultimate goal is to display the information and also to be able to search the info based on certain things like the year or artist name or genre. That part of the program I feel comfortable with, but the file reading is really stumping me. A nudge in the right direction would greatly appreciated. Thanks in advance.
I think your approach is good so far. In the struct Discography maybe you could use std::unordered_map<int, std::string> instead, but that's just my opinion. If you know in advance the number of input lines your data is composed of, I strongly recommend changing your istream& operator>>(istream& input, Artist& artist) to use std::getline:
std::istream& operator>>(std::istream& input, Artist& artist)
{
std::string line;
std::getline(input, line);
artist.artistName = line;
std::getline(input, line);
auto genres = splitString(line, ';');
artist.genres = genres;
std::getline(input, line);
artist.startingYear = std::stoi(line);
// etc ...
return input;
}
To implement std::vector<std::string> splitString(const std::string& str, char delim); you can use this answer. Then you can also think of all kinds of error handling like invalid integer conversion, empty line, not enough lines, etc.

Reading Comma Delimited Text File Into Array

I am trying to write a program in C++ that emulates a college enrollment system, where the student enters their ID, and the program searches a text file for their information, and loads a struct based on the text file. I have gotten to a point where I am having trouble getting their enrolled courses into the struct's array. Using the getline function, using the ',' as the delim will also carry over the next line until the next comma. What would be the correct algorithm for this?
This is the file setup that contains fake student information:
918273645,Steve,Albright,ITCS2530,MATH210,ENG140
123456789,Kim,Murphy,ITCS2530,MATH101
213456789,Dean,Bowers,ITCS2530,ENG140
219834765,Jerry,Clark,MGMT201,MATH210
(Bullets added for layout; not in the file)
For example, the user enters "123456789" for their ID, and Kim Murphy's information is then read. Upon the first iteration of getline, "ITCS2530" is read and put into the variable, and is then loaded into the struct; no problem there. However, the last course in the list has the newline character before the next comma, so the next iteration reads "MATH101/nl213456789" and puts the entire string into the variable and tries to load that into the struct.
The first column is their ID, then their First name, last name, and then their currently-enrolled courses after that. Notice that the number of enrolled courses can vary.
Here is the code that I am currently working on:
student login()
{
string ID;
student newStudent;
string enrolled;
int i = 0;
while (true)
{
cout << "Enter Student ID: ";
cin >> newStudent.ID;
cout << endl;
if (newStudent.ID.length() == 9)
break;
else
cout << "That ID is invalid - IDs are 9 digits" << endl;
}
ifstream inFile;
ofstream outFile;
inFile.open("registration.txt");
if (inFile.is_open())
//Check if file is open
{
while (!inFile.eof())
//While not at end of file
{
getline(inFile, ID, ',');
//Search for ID
if (ID == newStudent.ID)
{
getline(inFile, newStudent.fName, ',');
//Assign fName and lName
getline(inFile, newStudent.lName, ',');
while (enrolled != "\n")
{
getline(inFile, enrolled, ',');
if (enrolled == "\n")
{
cout << "Not currently enrolled in a class." << endl;
}
else
{
newStudent.courses[i] = enrolled;
i++;
}
}
cout << newStudent.lName << ", Welcome to the MCC Enrollment System!" << endl;
for (i = 0; i <= NUM_OF_COURSES; i++)
{
cout << "Enrolled courses: " << newStudent.courses[i] << endl;
}
cout << endl;
break;
//Stops searching
}
else
//Ignores rest of line - used to skip to the next line
{
getline(inFile, ID, '\n');
}
if (inFile.eof())
//If ID was not found
{
inFile.close();
cout << "Enter First Name: ";
//Begin entry of new student
cin >> newStudent.fName;
cout << endl;
cout << "Enter Last Name: ";
cin >> newStudent.lName;
cout << endl;
outFile.open("registration.txt", ios::app);
if (outFile.is_open())
{
outFile << newStudent.ID << "," << newStudent.fName << "," << newStudent.lName << "\n";
}
}
}
}
return newStudent;
}
Thanks in advance for any help.
The problem is that std::getline takes exactly one character as a delimiter. It defaults to a newline but if you use another character then newline is NOT a delimiter any more and so you end up with newlines in your text.
The answer is to read the entire line into a string using std::getline with the default (newline) delimiter and then use a string stream to hold that line of text so you can call std::getline with a comma as a delimiter.
Something like this:
#include <fstream>
#include <sstream>
#include <iostream>
#include <vector>
int main()
{
std::ifstream inFile("registration.txt");
if (inFile.is_open())
{
std::string line;
while( std::getline(inFile,line) )
{
std::stringstream ss(line);
std::string ID, fname, lname;
std::getline(ss,ID,','); std::cout<<"\""<<ID<<"\"";
std::getline(ss,fname,','); std::cout<<", \""<<fname<<"\"";
std::getline(ss,lname,','); std::cout<<", \""<<lname<<"\"";
std::vector<std::string> enrolled;
std::string course;
while( std::getline(ss,course,',') )
{
enrolled.push_back(course); std::cout<<", \""<<course<<"\"";
}
std::cout<<"\n";
}
}
return 0;
}
In this example I am writing the text to the screen surrounded by quotes so you can see what is read.
split(string, seperator)
split("918273645,Steve,Albright,ITCS2530,MATH210,ENG140", ",")
split("123456789,Kim,Murphy,ITCS2530,MATH101", ",")
split("213456789,Dean,Bowers,ITCS2530,ENG140", ",")
split("219834765,Jerry,Clark,MGMT201,MATH210", ",")
I know very little C++, but I remember this command.

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 can we create a loop so that user can input a name until it matches any one name included in text file using c++

I want user to input a name that we already had listed in text file. Until a user give a name that matches with a name which contain in our 'itemlist.txt' file user have to input another name and when a name matches the loop should be break. I was trying to do like this....
#include <iostream>
#include <fstream>
using namespace std;
int production, price;
string category, item, itemname;
int main(){
ifstream findItem("itemlist.txt");
while(true){
cout << "item: ";
cin >> itemname;
while(findItem >> category >> item >> production >> price){
if(itemname==item){
break;
}
}
if(itemname==item){
break;
}
cout << "Item couldn't be found in our data base." << endl;
}
}
As you have it now, you only get one pass through the file. If the first item entered is not in the file, you reach the end and cannot read any more. A very minor tweak will make your program work (albeit, very inefficiently). Just put the ifstream creation inside the loop.
while(true){
ifstream findItem("itemlist.txt");
...
That way, you are opening and reading through the file each time through the loop.
Don't do that. It is extremely inefficient. A much better solution is to read the contents of the file (or at least, the parts that are necessary) into a data structure which can be searched efficiently, such as a hash set (e.g. std::unordered_set from the standard library <unordered_set> header).
std::ifstream findItem("itemlist.txt");
std::unordered_set<std::string> items;
while(findItem >> category >> item >> production >> price)
items.insert(item);
Then you can search for your item from this set.
while(true){
std::cout << "item: ";
std::cin >> itemname;
if (items.count(itemname))
break;
std::cout << "Item couldn't be found in our data base." << std::endl;
}
Every time someone enters an invalid item, you go through the entire file. Then, when another item gets entered, the file pointers points the the end. You need to rewind the stream by putting this at the beginning of the while (true) loop:
findItem.gseek(0);
Personally, however, I would write the code to load the items into memory once:
struct Item {
Item(string s) {
stringstream ss;
ss << s;
ss >> category >> item >> production >> price;
}
bool operator==(string s) {
return item == s;
}
string category, item;
int production, price;
};
int main() {
ifstream file("itemlist.txt");
vector<Item> items;
string cur;
while (getline(file, cur))
items.emplace_back(cur);
string item;
while (true) {
cout << "item: ";
cin >> item;
std::vector<Item>::iterator it = find(items.begin(), items.end(), item);
if (it == items.end())
cout << "item not found";
else break;
}
}

Example for file input to structure members?

I have the following structure:
struct productInfo
{
int item;
string details;
double cost;
};
I have a file that will input 10 different products that each contain an item, details, and cost. I have tried to input it using inFile.getline but it just doesn't work. Can anyone give me an example of how to do this? I would appreciate it.
Edit
The file contains 10 lines that look like this:
570314,SanDisk Sansa Clip 8 GB MP3 Player Black,55.99
Can you provide an example please.
Edit
Sorry guys, I am new to C++ and I don't really understand the suggestions. This is what I have tried.
void readFile(ifstream & inFile, productInfo products[])
{
inFile.ignore(LINE_LEN,'\n'); // The first line is not needed
for (int index = 0; index < 10; index++)
{
inFile.getline(products[index].item,SIZE,DELIMETER);
inFile.getline(products[index].details,SIZE,DELIMETER);
inFile.getline(products[index].cost,SIZE,DELIMETER);
}
}
This is another approach that uses fstream to read the file and getline() to read each line on the file. The parsing of the line itself was left out on purpose since other posts have already done that.
After each line is read and parsed into a productInfo, the application stores it on a vector, so all products could be accessed in memory.
#include <iostream>
#include <fstream>
#include <vector>
#include <iterator>
#include <string>
using namespace std;
struct productInfo
{
int item;
string details;
double cost;
};
int main()
{
vector<productInfo> product_list;
ifstream InFile("list.txt");
if (!InFile)
{
cerr << "CouldnĀ“t open input file" << endl;
return -1;
}
string line;
while (getline(InFile, line))
{ // from here on, check the post: How to parse complex string with C++ ?
// https://stackoverflow.com/questions/2073054/how-to-parse-complex-string-with-c
// to know how to break the string using comma ',' as a token
cout << line << endl;
// productInfo new_product;
// new_product.item =
// new_product.details =
// new_product.cost =
// product_list.push_back(new_product);
}
// Loop the list printing each item
// for (int i = 0; i < product_list.size(); i++)
// cout << "Item #" << i << " number:" << product_list[i].item <<
// " details:" << product_list[i].details <<
// " cost:" << product_list[i].cost << endl;
}
EDIT: I decided to take a shot at parsing the line and wrote the code below. Some C++ folks might not like the strtok() method of handling things but there it is.
string line;
while (getline(InFile, line))
{
if (line.empty())
break;
//cout << "***** Parsing: " << line << " *****" << endl;
productInfo new_product;
// My favorite parsing method: strtok()
char *tmp = strtok(const_cast<char*>(line.c_str()), ",");
stringstream ss_item(tmp);
ss_item >> new_product.item;
//cout << "item: " << tmp << endl;
//cout << "item: " << new_product.item << endl;
tmp = strtok(NULL, ",");
new_product.details += tmp;
//cout << "details: " << tmp << endl;
//cout << "details: " << new_product.details << endl;
tmp = strtok(NULL, " ");
stringstream ss_cost(tmp);
ss_cost >> new_product.cost;
//cout << "cost: " << tmp << endl;
//cout << "cost: " << new_product.cost << endl;
product_list.push_back(new_product);
}
It depends on what's in the file? If it's text, you can use the redirect operator on a file input stream:
int i;
infile >> i;
If it's binary, you can just read it in to &your_struct.
You have to
0) Create a new instance of productInfo, pinfo;
1) read text (using getline) to the first comma (','), convert this string to an int, and put it into pinfo.item.
2) read text to the next comma and put it into pinfo.details;
3) read text to the endline, convert the string to a double, and put it into pinfo.cost.
Then just keep doing this until you reach the end of the file.
Here is how I would use getline. Note that I use it once to read from the input file, and then again to chop that line at ",".
ostream& operator>>(istream& is, productInfo& pi)
{
string line;
getline(is, line); // fetch one line of input
stringstream sline(line);
string item;
getline(sline, item, ',');
stringstream(item) >> pi.item; // convert string to int
getline(sline, item, ',');
pi.details = item; // string: no conversion necessary
getline(sline, item);
stringstream(item) >> pi.cost; // convert string to double
return is;
}
// usage:
// productInfo pi; ifstream inFile ("inputfile.txt"); inFile >> pi;
N.b.: This program is buggy if the input is
99999,"The Best Knife, Ever!",16.95