I have a text file that I need to read in to variables in my code. For example lets say the .txt file looks like:
John
Town
12
Mike
Village
22
where there is a pattern of name then address then age of multiple people. I found that with (`)
string line;
ifstream myfile ("example.txt");
if (myfile.is_open())
{
while ( getline (myfile,line) )
{
cout << line << '\n';
}
myfile.close();
}
I can print out each line of the text file, but how could I assign the text to a variable?
I remember in Java you could do something along the lines of
while(there is a next line){
name = something.readline();
address = something.readline();
age = something.readline();
//do something with variables i.e construct new object then
//re-loop to construct new object with next set of data
}
the trick was that after readline() was called it would then move down a line in the text file and then next variable would be assign to the text below and so on. How can I recreate this in C++?
When I do stuff like this, I like to structure my data into records and write a function to read each record rather like this:
// logically grouped data
struct record
{
std::string name;
std::string address;
unsigned age;
};
// function to read in one record
// returns std:ostream& so that the while() loop can check
// the stream to make sure the read was successful.
// Takes record as a reference to pass the data back out
// of the function
std::istream& read(std::istream& is, record& r)
{
std::getline(is, r.name);
std::getline(is, r.address);
is >> r.age >> std::ws;
return is;
}
int main()
{
std::ifstream myfile("example.txt");
record r;
while(read(myfile, r)) // while the read was a success
{
// do something with record here
std::cout << " name: " << r.name << '\n';
std::cout << "address: " << r.address << '\n';
std::cout << " age: " << r.age << '\n';
std::cout << '\n';
}
}
Related
I want to display the text file in my c++ program but nothing appears and the program just ended. I am using struct here. I previously used this kind of method, but now I am not sure why it isn't working. I hope someone could help me. Thanks a lot.
struct Records{
int ID;
string desc;
string supplier;
double price;
int quantity;
int rop;
string category;
string uom;
}record[50];
void inventory() {
int ID, quantity, rop;
string desc, supplier, category, uom;
double price;
ifstream file("sample inventory.txt");
if (file.fail()) {
cout << "Error opening records file." <<endl;
exit(1);
}
int i = 0;
while(! file.eof()){
file >> ID >> desc >> supplier >> price >> quantity >> rop >> category >> uom;
record[i].ID = ID;
record[i].desc = desc;
record[i].supplier = supplier;
record[i].price = price;
record[i].quantity = quantity;
record[i].rop = rop;
record[i].category = category;
record[i].uom = uom;
i++;
}
for (int a = 0; a < 15; a++) {
cout << "\n\t";
cout.width(10); cout << left << record[a].ID;
cout.width(10); cout << left << record[a].desc;
cout.width(10); cout << left << record[a].supplier;
cout.width(10); cout << left << record[a].price;
cout.width(10); cout << left << record[a].quantity;
cout.width(10); cout << left << record[a].rop;
cout.width(10); cout << left << record[a].category;
cout.width(10); cout << left << record[a].uom << endl;
}
file.close();
}
Here is the txt file:
Here are a couple of things you should consider.
Declare the variables as you need them. Don’t declare them at the top of your function. It makes the code more readable.
Use the file’s full path to avoid confusions. For instance "c:/temp/sample inventory.txt".
if ( ! file ) is shorter.
To read data in a loop, use the actual read as a condition while( file >> ID >>... ). This would have revealed the cause of your problem.
Read about the setw manipulator.
file's destructor will close the stream - you don't need to call close()
Your file format consists of a header and data. You do not read the header. You are trying to directly read the data. You try to match the header against various data types: strings, integers, floats; but the header is entirely made of words. Your attempt will invalidate the stream and all subsequent reading attempts will fail. So, first discard the header – you may use getline.
Some columns contain data consisting of more than one word. file >> supplier reads one word, not two or more. So you will get "Mongol", not "Mongol Inc." Your data format needs a separator between columns. Otherwise you won’t be able to tell where the column ends. If you add a separator, again, you may use getline to read fields.
The CATEGORY column is empty. Trying to read it will result in reading from a different column. Adding a separator will also solve the empty category column problem.
This is how your first rows will look like if you use comma as separator:
ID,PROD DESC,SUPPLIER,PRICE,QTY,ROP,CATEGORY,UOM
001,Pencil,Mongol Inc.,8,200,5,,pcs
A different format solution would be to define a string as a zero or more characters enclosed in quotes:
001 "Pencil" "Mongol Inc." 8 200 5 "" "pcs"
and take advantage of the quoted manipulator (note the empty category string):
const int max_records_count = 50;
Record records[max_records_count];
istream& read_record(istream& is, Record& r) // returns the read record in r
{
return is >> r.ID >> quoted(r.desc) >> quoted(r.supplier) >> r.price >> r.quantity >> r.rop >> quoted(r.category) >> quoted(r.uom);
}
istream& read_inventory(istream& is, int& i) // returns the number of read records in i
{
//...
for (i = 0; i < max_records_count && read_record(is, records[i]); ++i)
; // no operation
return is;
}
Unfortunately you text file is not a typical CSV file, delimited by some character like a comma or such. The entries in the lines seem to be separated by tabs. But this is a guess by me. Anyway. The structure of the source file makes it harder to read.
Additionally, the file has an header and while reading the first line andtry to read the word "ID" into an int variable, this conversion will fail. The failbit of the stream is set, and from then on all further access to any iostream function for this stream will do nothing any longer. It will ignore all your further requests to read something.
Additional difficulty is that you have spaces in data fields. But the extractor operator for formatted input >> will stop, if it sees a white space. So, maybe only read half of the field in a record.
Solution: You must first read the header file, then the data rows.
Next, you must know if the file is really tab separated. Sometimes tabs are converted to spaces. In that case, we would need to recreate the start position of a field in the a record.
In any case, you need to read a complete line, and after that split it in parts.
For the first solution approach, I assume tab separated fields.
One of many possible examples:
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
#include <iomanip>
const std::string fileName{"r:\\sample inventory.txt"};
struct Record {
int ID;
std::string desc;
std::string supplier;
double price;
int quantity;
int rop;
std::string category;
std::string uom;
};
using Database = std::vector<Record>;
int main() {
// Open the source text file with inventory data and check, if it could be opened
if (std::ifstream ifs{ fileName }; ifs) {
// Here we will store all data
Database database{};
// Read the first header line and throw it away
std::string line{};
std::string header{};
if (std::getline(ifs, header)) {
// Now read all lines containing record data
while (std::getline(ifs, line)) {
// Now, we read a line and can split it into substrings. Assuming the tab as delimiter
// To be able to extract data from the textfile, we will put the line into a std::istrringstream
std::istringstream iss{ line };
// One Record
Record record{};
std::string field{};
// Read fields and put in record
if (std::getline(iss, field, '\t')) record.ID = std::stoi(field);
if (std::getline(iss, field, '\t')) record.desc = field;
if (std::getline(iss, field, '\t')) record.supplier = field;
if (std::getline(iss, field, '\t')) record.price = std::stod(field);
if (std::getline(iss, field, '\t')) record.quantity = std::stoi(field);
if (std::getline(iss, field, '\t')) record.rop = std::stoi(field);
if (std::getline(iss, field, '\t')) record.category = field;
if (std::getline(iss, field)) record.uom = field;
database.push_back(record);
}
// Now we read the complete database
// Show some debug output.
std::cout << "\n\nDatabase:\n\n\n";
// Show all records
for (const Record& r : database)
std::cout << std::left << std::setw(7) << r.ID << std::setw(20) << r.desc
<< std::setw(20) << r.supplier << std::setw(8) << r.price << std::setw(7)
<< r.quantity << std::setw(8) << r.rop << std::setw(20) << r.category << std::setw(8) << r.uom << '\n';
}
}
else std::cerr << "\nError: COuld not open source file '" << fileName << "'\n\n";
}
But to be honest, there are many assumptions. And tab handling is notoriously error prone.
So, let us make the next approach and extract the data according to their position in the header string. So, we will check, where each header string starts and use this information to later split a complete line into substrings.
We will use a list of Field Descriptors and search for their start position and width in the header line.
Example:
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
#include <iomanip>
#include <array>
const std::string fileName{"r:\\sample inventory.txt"};
struct Record {
int ID;
std::string desc;
std::string supplier;
double price;
int quantity;
int rop;
std::string category;
std::string uom;
};
constexpr size_t NumberOfFieldsInRecord = 8u;
using Database = std::vector<Record>;
int main() {
// Open the source text file with inventory data and check, if it could be opened
if (std::ifstream ifs{ fileName }; ifs) {
// Here we will store all data
Database database{};
// Read the first header line and throw it away
std::string line{};
std::string header{};
if (std::getline(ifs, header)) {
// Analyse the header
// We have 8 elements in one record. We will store the positions of header items
std::array<size_t, NumberOfFieldsInRecord> startPosition{};
std::array<size_t, NumberOfFieldsInRecord> fieldWidth{};
const std::array<std::string, NumberOfFieldsInRecord> expectedHeaderNames{ "ID","PROD DESC","SUPPLIER","PRICE","QTY","ROP","CATEGORY","UOM"};
for (size_t k{}; k < NumberOfFieldsInRecord; ++k)
startPosition[k] = header.find(expectedHeaderNames[k]);
for (size_t k{ 1 }; k < NumberOfFieldsInRecord; ++k)
fieldWidth[k - 1] = startPosition[k] - startPosition[k - 1];
fieldWidth[NumberOfFieldsInRecord - 1] = header.length() - startPosition[NumberOfFieldsInRecord - 1];
// Now read all lines containing record data
while (std::getline(ifs, line)) {
// Now, we read a line and can split it into substrings. Based on poisition and field width
// To be able to extract data from the textfile, we will put the line into a std::istrringstream
std::istringstream iss{ line };
// One Record
Record record{};
std::string field{};
// Read fields and put in record
field = line.substr(startPosition[0], fieldWidth[0]); record.ID = std::stoi(field);
field = line.substr(startPosition[1], fieldWidth[1]); record.desc = field;
field = line.substr(startPosition[2], fieldWidth[2]); record.supplier = field;
field = line.substr(startPosition[3], fieldWidth[3]); record.price = std::stod(field);
field = line.substr(startPosition[4], fieldWidth[4]); record.quantity = std::stoi(field);
field = line.substr(startPosition[5], fieldWidth[5]); record.rop = std::stoi(field);
field = line.substr(startPosition[6], fieldWidth[6]); record.category = field;
field = line.substr(startPosition[7], fieldWidth[7]); record.uom = field;
database.push_back(record);
}
// Now we read the complete database
// Show some debug output.
std::cout << "\n\nDatabase:\n\n\n";
// Header
for (size_t k{}; k < NumberOfFieldsInRecord; ++k)
std::cout << std::left << std::setw(fieldWidth[k]) << expectedHeaderNames[k];
std::cout << '\n';
// Show all records
for (const Record& r : database)
std::cout << std::left << std::setw(fieldWidth[0]) << r.ID << std::setw(fieldWidth[1]) << r.desc
<< std::setw(fieldWidth[2]) << r.supplier << std::setw(fieldWidth[3]) << r.price << std::setw(fieldWidth[4])
<< r.quantity << std::setw(fieldWidth[5]) << r.rop << std::setw(fieldWidth[6]) << r.category << std::setw(fieldWidth[7]) << r.uom << '\n';
}
}
else std::cerr << "\nError: COuld not open source file '" << fileName << "'\n\n";
}
But this is still not all.
We should wrap all functions belonging to a record into the struct Record. And the same for the data base. And espcially we want to overwrite the extractor and the inserter operator. Then input and output will later be very simple.
We will save this for later . . .
If you can give more and better information regarding the structure of the source file, then I will update my answer.
Hi guys I'm working on my last assignment of the computer science class. I think I'm doing everything fine but something is wrong. Can you take a look at it and tell what I'm doing wronger here.
here is what I get when I try to submit online on zybooks site:
"Your program produced no output"
Expected:
Ryan Hermle
22.99
Lochness Monster
3.50
Wonder Woman
123456.78
here are instructions from professor:
Constructor:
Takes a string parameter and stores that as fileName. Does not need to do anything else.
append:
Takes a record as a parameter that contains a string and a double
Open an output file stream in append mode using fileName
set its precision to 2 and fixed
Output the name, newline, the money, newline
searchName:
Open an input file stream with fileName
Loop while a getline and a double extraction are successful
if the string parameter is equal to the name read from the getline, then return the double
If the loop finishes without finding anything, return -1 to indicate that name was not found.
getData:
Open an input file stream with fileName
Construct an ostringstream
Set its precision to 2 and fixed
Loop while a getline and a double extraction are successful
ignore the \n left by the >> extraction
write the string, newline, double, newline to the ostringstream
return the string contained by the ostringstream
here is my main:
#include "Database.h"
int main()
{
Database db("data.txt");
db.append(Record{"Ryan Hermle", 22.99});
db.append(Record{"Lochness Monster", 3.50});
db.append(Record{"Wonder Woman", 123456.78});
}
and here is my Database.cpp file:
#include "Database.h"
Database::Database(string file)
{
fileName = file;
}
void Database::append(Record data)
{
ofstream out;
out.open(fileName, ios::app);
out << setprecision(2) << fixed;
cout << data.name << endl;
cout << data.money << endl;
out.close();
}
double Database::searchName(string n)
{
Record s;
ifstream in;
in.open(fileName);
while (getline(in, n) >> s.money)
{
in.ignore();
if (n == s.name)
{
return s.money;
}
}
return -1;
}
string Database::getData()
{
Record s;
ifstream ifs;
ifs.open(fileName);
ostringstream oss;
oss << setprecision(2) << fixed;
while(getline(ifs, s.name) >> s.money)
{
ifs.ignore();
oss << s.name << endl << s.money << endl;
cout << oss.str();
}
return oss.str();
}
Thanks for everyone who replied to my post. I was able figure out the error in my program.
The error was in append Function:
cout << data.name << endl;
cout << data.money << endl;
It should be like this:
out << data.name << endl;
out << data.money << endl;
I'm working on this program that is to help manage a DVD rental store. What I have to do is take a text that contains info about DVD etc:
Mean girls; comedy; PG; 2009; Regina George; 12.07.2015;
The Conjuring; Horror; R; 2013; Sara Johnson; 16.05.2016;
Pokemon 2000; Kids; G; 2000; Ash Katchem; 15.04.2016;
etc..
And then takes this information and then reads it into an array and from there the array is read into the struct and then displayed in proper order like so:
Name: Mean Girls
Genre: Comedy
Rating: PG
etc...
This is my code so far:
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
using namespace std;
struct dvd{
string name;
string genre;
string rating;
string released;
string renter;
string rentday;
void print();
bool read(ifstream & file);
};
void dvd::print(){
cout <<"Title: " << name << endl;
cout <<"Genre: " << genre << endl;
cout << "Rating: " << rating << endl;
cout << "Release date: " << released << endl;
cout << "Name of renter: " << renter << endl;
cout << "Date rented: " << rentday << endl;
}
bool dvd::read(ifstream & file)
{
getline(file, name, ';');
getline(file, genre, ';');
getline(file, rating,';');
getline(file, released, ';');
getline(file, renter, ';');
getline(file, rentday, ';');
return file.good();
}
int main() {
vector<dvd> dvds;
dvd dvd1;
ifstream file("DVD.txt");
if(! file.is_open()){
cout << "Failed to find input file" << endl;
return 1;
}
while(dvd1.read(file))
{ dvds.push_back(dvd1);
}
dvd1.print();
return 0;
}
So what I would like to do is have read the text file into the Array and from there read the Array into the struct. So instead of the text file reading into the vector I need it to read into the array and from there read the first line of the array (dvdArray[1]) into struct dvd and then print out that information using print function and then loop that until dvdArray[10] is read into struct dvd!
Thank you so much for your help! :)
Change
bool dvd::read(ifstream & file)
to
bool dvd::read(istream & file)
No other changes to its contents are required.
Then, take each line and put it into a std::istringstream, then pass it to dvd::read.
You should be able to figure out the rest on your own.
For simple reading from and writing to file, I would suggest overload << and >> for your struct class, in order to make the code easy to serialize and de-serialize in a readable fashion.
friend std::ostream& operator<< (std::ostream& stream, const dvd& dvdObj)
{
// your output stuff
// stream <<"Title: " << dvdObj.name << endl;
// ...
return stream;
}
friend std::istream& operator>> (std::istream& stream, dvd& dvdObj)
{
// your output stuff
// getline(stream, dvdObj.name, ';');
// ...
return stream;
}
Then,
// look for std::copy for reading directly into vector ... else
while( file >> dvd1 )
{
dvds.push_back(dvd1);
}
And,
for( const auto& dvd1: dvds )
{
std::cout << dvd1 ;
}
I have a .txt file with names and grades such as "emiltytaco 56". After the last name, there are 3 blank lines which should not be inserted into my trie and heap. However the code is Aborted and dumped when hitting the blank lines. This is the insert function
while(myfile.good())
{
getline(myfile, line);
name = line.substr(0,line.find("\t"));
stringstream convert((line.substr(line.find("\t")+1)));
convert >> grade;
if(name.at(0) > 96 && name.at(0) < 123)
{
insert(name, grade);
cout << name << " " << grade << endl;
}
}
myfile.close();
should the .close be part of an "else" statement with the if? a friend of mine has this exact thing but his does not abort.
First point, change your loop to something like:
while (getline(myfile, line)) {
// ...
}
Second, it's probably a lot simpler to feed the whole line to the stringstream, and read both pieces from there:
stringstream convert(line);
std::getline(convert, name, '\t');
convert >> grade;
Third, if you want to check for lower-case letters, you're much better off with islower than comparisons to magic numbers like 96 and 123. I wouldn't do the check that way though. If you want to check for a blank line, it would be better to do that directly, with line.empty() immediately after you read it. Using that, you end up with something like:
while (getline(myfile, line))
if (!line.empty()) {
std::istringstream convert(line);
std::getline(convert(line, name);
convert >> grade;
insert(name, grade);
std::cout << name << " " << grade << "\n";
}
There is one more step beyond that though: since the name and grade are (apparently) related, I'd probably define a class for them, and write an extractor to get an object of that type from a stream:
class whatever {
std::string name;
int grade;
friend std::istream &operator>>(std::istream &is, whatever &w) {
std::getline(is, w.name, '\t');
is >> w.grade;
is.ignore(4096, '\n');
return is;
}
};
With that in place, we can read the data from the file quite a bit more simply:
whatever w;
while (myfile >> w) {
insert(w);
std::cout << w.name << " " << w.grade << "\n";
}
Note that in this case, we don't have to check for the blank lines explicitly at all -- we just check whether we could successfully read and convert a whatever from the stream, which will fail for a blank line.
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