Reading Lines from File and Storing in Separate String Variables - c++

I am creating a bank terminal for an assignment. It has the ability to add clients with each client containing 5 different variables for name, address, social#, employer, and income. Those variables are then written to a file once they have been filled and the terminal is exited.
The problem I am having is when starting the terminal I need to read these values from the file, each on their separate lines and store them in their respective variables to use in the addClient() function. This is the code snippet to make things easier than submitted my entire project:
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main()
{
using namespace std;
std::ifstream infile2("client-info.txt");
//Strings used for respective items from file
string clientName, clientAddress, clientSocial, clientEmployer, clientIncome;
//Here is where I am having the problem of reading the info from the file
//line by line and storing it in respective variables.
while (infile2)
{
getline(infile2,clientName);
getline(infile2,clientAddress);
getline(infile2,clientSocial);
getline(infile2,clientEmployer);
getline(infile2,clientIncome);
client.addClient(clientName, clientAddress, clientSocial, clientEmployer, clientIncome);
}
infile2.close();
}
The file, for example is stored as such.
John Doe
123 Easy Lane
123-45-6789
USSRC
36000
The problem I am having is that I cannot figure out a solid way to get each line and store them in their respective strings. For the assignment, I will not have to be dealing with blank spaces and such. So lines 0-4 will be for one client, 5-9 for another, etc.
A push in the right direction would be greatly appreciated, thanks!

If the addClient function accepts 5 parameters as you have did, then your current main function has already solved your questions.
If you would like to put those 5 strings into a single string, then work this single string inside addClient function.
You can create a class:
class ClientInfo
{
private:
string clientName;
string clientAddress;
string clientSocial;
string clientEmployer,;
string clientIncome;
public:
ClientInfo(string name, string addr, string ssn,
string employer, string income):
clientName(name), clientAddress(addr), clientSocial(ssn),
clientEmployer(employer), clientIncome(income)
{
}
};
Then inside your main, you can do the following:
ClientInfo currentClient(clientName, clientAddress,
clientSocial, clientEmployer, clientIncome);
client.addClient(currentClient);

I think the only problem you're having is when you call getline, you're not passing in a parameter. In this case I think you need to use the newline delimeter.
while (infile2)
{
getline(infile2,clientName, '\n');
getline(infile2,clientAddress, '\n');
getline(infile2,clientSocial, '\n');
getline(infile2,clientEmployer, '\n');
getline(infile2,clientIncome, '\n');
client.addClient(clientName, clientAddress, clientSocial, clientEmployer, clientIncome);
}
I'm not sure on the '\n' syntax but this will read the file until it hits a newline and then move on to the next line.

Related

Extract JSON data from file in C++

Here there, sorry if this question is not well-suited for this forum. I'm pretty new to programming and thought I'd get a better command of strings and files by creating this little project. What I'm trying to do is extract data from a JSON document. Eventually I'd store the data in an array I suppose and work with it later.
Basically, I'm wondering if there is a better way of going about this. The code seems kind of wordy and definitely not elegant. Again, sorry if this question is not a good one, but I figured there'd be no better way to learn than through a community like this.
#include <iostream>
#include <fstream>
#include <cstring>
#include <string> //probably including more than necessary
using namespace std; //should be specifying items using scope resolution operator instead
int main(int argc, const char * argv[])
{
ifstream sfile("JSONdatatest.txt");
string line,temp;
while(!sfile.eof()){
getline(sfile, line);
temp.append(line); //creates string from file text, use of temp seems extraneous
}
sfile.close();
cout << "Reading from the file.\n";
size_t counter=0;
size_t found=0;
size_t datasize=0;
while(found!=string::npos && found<1000*70){ //problem here, program was creating infinite loop
//initial 'solution' was to constrain found var
//but fixed with if statement
found = temp.find("name: ",counter);
if(found!=string::npos){
found=found+7; //length of find variable "name: ", puts us to the point where data begins
size_t ended=temp.find_first_of( "\"", found);
size_t len=ended-found; //length of datum to extract
string temp2(temp, found, len); //odd use of a second temp function,
cout << temp2 << endl;
counter=ended+1;
datasize++; //also problem with data size and counter, so many counters, can they
//coordinate to have fewer?
}
}
cout << datasize;
return 0}
Where I indicate an infinite loop is made, I fixed by adding the if statement in the while loop. My guess is because I add 7 to 'found' there is a chance it skips over npos and the loop continues. Adding the if statement fixed it, but made the code look clunky. There has to be a more elegant solution.
Thanks in advance!
I would recommend that you use a third-party to do all this stuff, which is pretty tough with raw tools. I actually did this kind of stuff recently so I can give you some help.
I would recommend you take a look at boost::property_tree .
Here is the theory: A Json file is like a tree, you have a root, and many branches.
The idea is to transform this JSON file into a boost::property_tree::ptree, so then you use easily the object ptree and not the file.
First, let's say we have this JSON file:
{
"document": {
"person": {
"name": "JOHN",
"age": 21
},
"code": "AX-GFD123"
}
"body" : "none"
}
Then in your code, be sure to include:
#include "boost/property_tree/ptree.hpp"
#include "boost/property_tree/json_parser.hpp"
Then here is the most interesting part:
boost::property_tree::ptree root;
You create the ptree object named root.
boost::property_tree::read_json("/path_to_my_file/doc.json", root);
Then you tell what file to read, and where to store it (here in root). Be careful, you should use try / catch on this in case the file doesn't exist.
Then you will only use the root tree which is really easy to do. You have many functions (I invite you to see the boost documentation page).
You want to access the namefield. Right then do this:
std::string myname = root.get<std::string> ("document.person.name", "NOT FOUND");
The get function has the first parameter the path to get the attribute you want, the second is for default return if the path is incorrect or doesn't exist. the <std::string> is to show what type it must return.
Let's finish with another example. Let's say you want to check all your root nodes, that means every node which are on the top level.
BOOST_FOREACH(const boost::property_tree::ptree::value_type& child, root.get_child(""))
{ cout << child.first << endl; }
This is a bit more complicated. I explain. You tell boost to look every child of the root with root.get_child("") , "" is used for root. Then, for every child found, (like a basic iterator), you will use const boost::property_tree::ptree::value_type& child.
So inside the foreach, you will use the child to access whatever you want. child.firstwill give you the name of the child node currently in use. In my example it will print first document, and then body.
I invite you to have a look at Boost documentation. It looks maybe hard at first, but it is really easy to use after that.
http://www.boost.org/doc/libs/1_41_0/doc/html/property_tree.html

Solving an exercise from Thinking C++

The exercise says:
Create a Text class that contains a string object to hold the text of
a file. Give it two constructors: a default constructor and a
constructor that takes a string argument that is the name of the file
to open. When the second constructor is used, open the file and read
the contents into the string member object. Add a member function
contents() to return the string so (for example) it can be printed. In
main( ), open a file using Text and print the contents.
This is the class that I wrote:
class Text {
string fcontent;
public:
Text();
Text(string fname);
~Text();
string contents();
};
I haven't understood everything of this exercise. It asks to create a function contents(), that returns a string, but it doesn't says what the function has to do...
Neither what the default constructor has to do.
Could someone help me?
The function has to return the contents of the file, which is stored (in your case) in fcontents.
string Text::contents()
{
return fcontent;
}
The default constructor doesn't have to do anything in this case.
Text::Text(){}
EDIT:
Seeing how many comments there are below with new problems, I'm going to recap and answer the rest of the questions here.
in Text.h you have:
#ifndef TEXT_HH
#define TEXT_HH
#include <string> //[1]
class Text {
std::string fcontent;//[2]
public:
Text();
Text(std::string fname);
~Text();
std::string contents();
};
#endif
and Text.cpp has
// Text.cpp
#include "Text.h"
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
Text::Text() {}
Text::Text(string fname) {
fstream f;
f.open(fname.c_str(), ios::in);//[3]
//[4]
std::stringstream stream;
while(true)
{
char buffer[1000];
f.getline(buffer, 1000);
if(f.good())
{
//This actually adds an extra newline at the end
stream << buffer << '\n';
}
else
{
break;
}
}
fcontent = stream.str();
//remove extra newline
fcontent.erase(fcontent.begin() + fcontent.size() - 1);
f.close();//This is technically unnecessary, but not bad either
}
string Text::contents() {
return fcontent;
}
Text::~Text() {}//[5]
Point 1: The header file <string> contains the class definition for std::string, the C++ string. This should not be confused with <cstring> which contains functions for manipulating C strings (const char *, const char[], etc).
Point 2: The string class exists in the ::std namespace, which means we have to either use std::string every time we want that class or use using namespace std; to pull this class into the global scope. In the header file we prefer the former method because the using declaration doesn't go away, which means that the namespace will be changed for every header and source file that includes this one, which we want to avoid in general (ie. always). In the cpp file however, there is no problem using the using declaration and we do so.
Point 3: fstreams take a C string as the filename parameter, we can get the corresponding C string from a C++ string with the call c_str(). This returns a const char *.
Point 4: To read the whole text file into a string is less obvious than it seems because the way streams deal with eof (end-of-file) and state-checking stuff. In short it will read one more time than you want it to (I know, wanting is subjective, but is close enough I think) before setting the eof flag. That's why the state is checked after calling get and before adding what's been read to our stringstream. Streams are a fairly elaborate topic so I won't go into it in more detail here.
Point 5: Destructors on objects (non-pointers, like our fcontents is) are called automatically, so we don't need to do anything to make sure that our fcontents string is destroyed when our Text object is destroyed. When we allocate something dynamically with new that's when we have to worry about calling delete on it when we want to destroy it.

How do i write linked list as an object to a file

In this function I am trying to write the information from the linked list to a file
class Employer: public People{
void tryWrite(){
ofstream file("Employer.dat",ios::out|ios::app| ios::binary);
if(file.is_open()){
file<<getFirstName()<<" "<<getLastName()<<" " << companyName<<" "<<position<<" "<<getTelHome()<<" "<<getTelMobile()<<" " <<email<<" "<<getAddress()<< endl;
file.close();
}
}
};
#endif
This is the linked list manipulator for the employer class
At this point in the writeFile function I am trying to traverse through the link list and call the tryWrite function in Employer.h to write to the actual data in the link list to file
class EmployerList
{
private:
Employer *head;//employee pointer(head pointer)
public:
void writeFile(){
Employer *temp = head;
while (temp != NULL)
{
temp->tryWrite();//Employee's display method called here
temp = temp->getNext();
}
}
In this read function I would like to read the data in the following order Name: Jane brown Company's name: Lime Position: clerk Address:
New Kingston Home#: 876-466-8778 Mobile #: 543-455-6344 Email: dumb#yahoo.com
But reading directly from the file the data is Jane brown Lime clerk
New Kingston 876-466-8778 543-455-6344 dumb#yahoo.com
when it gets to the address it will display as: Address: New
instead of: Address: New Kingston
The null spaces is creating the issue and i do not know how to get it
to do what i want
void TestRead(){
ifstream tryRead("em.dat", ios::in|ios::binary);
string Email, cName, pos,fName,lName,addr, tHome,tMobile;
while(tryRead>>fName>>lName>>cName>>pos>>addr>>tHome>>tMobile){
cout<<"Name: "<<fName<<lName<<endl;
cout<<"Comapny's Name: "<<cName<<endl;
cout<<"Position: "<<pos<<endl;
cout<<"Address: "<<addr<<endl;
cout<<"Home#: "<<tHome<<endl;
cout<<"Mobile#: "<<tMobile<<endl;
tryRead.close();
}
}
};
#endif
This is main which populate the linked list with data
#include <iostream>
#include <string>
#include "Employer.h"
#include "EmployerList.h"
using namespace std;
void main()
{
EmployerList em;
em.AddNode("nim#yahoo.com", "LIME", "Manager", "Tashauna", "Simon", "New Kingston", "876-985-8337", "876-405-3536");
em.AddNode("ker#gmail.com", "NOKIA", "CEO", "Keric", "McDonald", "Europe", "0411-789-6548", "985-257-1111");
After the linked list is filled with data the write file function in EmployerList.h is called
em.writeFile();
em.TestRead();
system("pause");
}
I didn't read your code, because its irrelevant. The question is in the title, and that's what I'm answering.
Writing a linked list into a file means writing each member of the linked list into a file. Linked list is a chain of objects that point one to the next one, and when you want to dump it into a file - you just dump each of these objects one after another.
When you need to rebuild your linked list - you read each object from the file, and chain it in a new linked list in the same order (if you saved FIFO - make sure to rebuilt the same, not in reverse). Needless to mention, you should not be saving the pointers and reading them from the file back.
If the question is about something in that enormous chunk of code - you'll have to refine the question and minimize the code.
edit
After you kindly mentioned what the real problem is - then in your tryWrite you're opening the same file over and over again, overwriting whatever was there previously. Obviously, it will leave you with the one last object from the list in the end, because all the previous ones would be overwritten.
You should use ios::out | ios::in | ios::ate or ios::out | ios::app, to seek to the end of the existing data and add, instead of just ios::out.
As far as I can tell without transcribing, compiling and running your code, there isn't anything fundamentally wrong with it. I would have extracted the ofstream out of the tryWrite() function, opened it once in writeFile() and pass it in as an argument, rather than opening and closing the file for each node in the linked list.
You MIGHT have some problems reading the linked list back in because you don't seem to do anything to scan for illegal characters in the data. You are using the space character as a delimiter, so you should be careful to make sure that any fields that you save to the file are first escaped (i.e. that you replace any space characters with some kind of escape sequence). Otherwise, when reading things back in, a wayward space could throw things off badly.
For future reference, if you are having problems with your code, rather than just listing all the code and asking what's wrong with it, you should probably try to at least describe what problems you're running into -- what is the output, where in the code do you think you are doing something wrong, etc.

Parse through a text file, creating instance of new object c++

I need to parse through a text file that contains something like :
1|Song Title|Release date||"ignore me"|0|0|0|1|1|1|0|0|0|0|0|0|0|0|0|0|0|0|0
which is the song number, followed by the release date, followed by a website that I need to ignore, and followed by a series of 0's and 1's which could represent an vector of genres.
I need a way to separate this data, and ignore the one that say's the website while at the same time creating a new instance of a Song Object which has an : (int songNumber,string songTitle, vector* genres, string releaseDate)
Thanks!
The C++ String Toolkit Library (StrTk) has the following solution to your problem:
#include <string>
#include <deque>
#include "strtk.hpp"
struct song_type
{
unsinged int id;
std::string release_date;
std::string url;
char genre[8];
};
strtk_parse_begin(song_type)
strtk_parse_type(id)
strtk_parse_type(release_date)
strtk_parse_type(url)
strtk_parse_type(genre[0])
strtk_parse_type(genre[1])
strtk_parse_type(genre[2])
strtk_parse_type(genre[3])
strtk_parse_type(genre[4])
strtk_parse_type(genre[5])
strtk_parse_type(genre[6])
strtk_parse_type(genre[7])
strtk_parse_end()
int main()
{
std::deque<song_type> song_list;
strtk::for_each_line("songs.txt",
[&song_list](const std::string& line)
{
song_type s;
if (strtk::parse(line,"|",s))
song_list.push_back(s);
});
return 0;
}
More examples can be found Here
Define a class Song that holds the data in the form you require, as you stated above
implement Song::operator>>(const istream&); to populate the class by parsing the above data from an input stream
read the file line by line using string::getline
for each line, convert to stringstream and then use your operator>> to fill in the fields in an instance of Song.
It's straightforward to tokenize the stringstream with the '|' character as a separator, which would be the bulk of the work.
int main()
{
std::string token;
std::string line("1|Song Title|Release date||\"ignore me\"|0|0|0|1|1|1|0|0|0|0|0|0|0|0|0|0|0|0|0");
std::istringstream iss(line);
while ( getline(iss, token, '|') )
{
std::cout << token << std::endl;
}
return 0;
}
Code lifted from here.
You'd typically do this by overloading operator>> for the type of object:
struct song_data {
std::string number;
std::string title;
std::string release_date;
// ...
};
std::istream &operator>>(std::istream &is, song_data &s_d) {
std::getline(is, s_d.number, '|');
std::getline(is, s_d.title, '|');
std::getline(is, s_d.release_date, '|');
std::string ignore;
std::getline(is, ignore, '|');
// ...
return is;
}
Depending on whether there are more fields you might want to ignore (especially trailing fields) it can sometimes be more convenient to read the entire line into a string, then put that into an istringstream, and parse the individual fields from there. In particular, this can avoid extra work reading more fields you don't care about, instead just going on to the next line when you've parsed out the fields you care about.
Edit: I would probably handle the genres by adding a std::vector<bool> genres;, and reading the 0's and 1's into that vector. I'd then add an enumeration specifying what genre is denoted by a particular position in the vector, so (for example) testing whether a particular song is classified as "country" would look something like:
enum { jazz, country, hiphop, classic_rock, progressive_rock, metal /*, ... */};
if (songs[i].genres[country])
if (songs[i].genres[hiphop])
process_hiphop(songs[i]);
Of course, the exact genres and their order is something I don't know, so I just made up a few possibilities -- you'll (obviously) have to use the genres (and order) defined for the file format.
As far as dealing with hundreds of songs goes, the usual way would be (as implied above) create something like: std::vector<song_data> songs;. Using a stream extraction like above, you can then copy the data from the file to the vector:
std::copy(std::istream_iterator<song_data>(infile),
std::istream_iterator<song_data>(),
std::back_inserter(songs));
If you're likely to look up songs primarily by name (for one example), you might prefer to use std::map<std::string, song_data> songs. This will make it easy to do something like:
songs["new song"].release_date = Today;

How to parse an XML file with RapidXml

I have to parse an XML file in C++. I was researching and found the RapidXml library for this.
I have doubts about doc.parse<0>(xml).
Can xml be an .xml file or does it need to be a string or char *?
If I can only use string or char * then I guess I need to read the whole file and store it in a char array and pass the pointer of it to the function?
Is there a way to directly use a file because I would need to change the XML file inside the code also.
If that is not possible in RapidXml then please suggest some other XML libraries in C++.
Thanks!!!
Ashd
RapidXml comes with a class to do this for you, rapidxml::file in the rapidxml_utils.hpp file.
Something like:
#include "rapidxml_utils.hpp"
int main() {
rapidxml::file<> xmlFile("somefile.xml"); // Default template is char
rapidxml::xml_document<> doc;
doc.parse<0>(xmlFile.data());
...
}
Note that the xmlFile object now contains all of the data for the XML, which means that once it goes out of scope and is destroyed the doc variable is no longer safely usable. If you call parse inside of a function, you must somehow retain the xmlFile object in memory (global variable, new, etc) so that the doc remains valid.
New to C++ myself... but I wanted to share a solution.
YMMV!
Shout Out to SiCrane on this thread:
-- and just replacing 'string' with a vector --- (thanks anno)
Please comment and help me learn also! I'm very new to this
Anyway, this seems to work for a good start:
#include <iostream>
#include <fstream>
#include <vector>
#include "../../rapidxml/rapidxml.hpp"
using namespace std;
int main(){
ifstream myfile("sampleconfig.xml");
rapidxml::xml_document<> doc;
/* "Read file into vector<char>" See linked thread above*/
vector<char> buffer((istreambuf_iterator<char>(myfile)), istreambuf_iterator<char>( ));
buffer.push_back('\0');
cout<<&buffer[0]<<endl; /*test the buffer */
doc.parse<0>(&buffer[0]);
cout << "Name of my first node is: " << doc.first_node()->name() << "\n"; /*test the xml_document */
}
We usually read the XML from the disk into a std::string, then make a safe copy of it into a std::vector<char> as demonstrated below:
string input_xml;
string line;
ifstream in("demo.xml");
// read file into input_xml
while(getline(in,line))
input_xml += line;
// make a safe-to-modify copy of input_xml
// (you should never modify the contents of an std::string directly)
vector<char> xml_copy(input_xml.begin(), input_xml.end());
xml_copy.push_back('\0');
// only use xml_copy from here on!
xml_document<> doc;
// we are choosing to parse the XML declaration
// parse_no_data_nodes prevents RapidXML from using the somewhat surprising
// behavior of having both values and data nodes, and having data nodes take
// precedence over values when printing
// >>> note that this will skip parsing of CDATA nodes <<<
doc.parse<parse_declaration_node | parse_no_data_nodes>(&xml_copy[0]);
For a complete source code check:
Read a line from xml file using C++
The manual tells us:
function xml_document::parse
[...] Parses zero-terminated XML string
according to given flags.
RapidXML leaves loading the character data from a file to you. Either read the file into a buffer, like anno suggested or alternatively use some memory mapping technique. (But look up parse_non_destructive flag first.)