I'm making a program for my c++ class. Ultimately I want my program to perform a quicksort on a text file of contacts in the following format:
Firstname Secondname Number
Each contact is separated by a new line. I've started by counting the number of lines and using dynamic memory allocation to create an array of structs which has the same size as the number of lines.
However, when I tried to read in the information from the text file and output it to the screen, all I get is gibberish. I've had a look around on the internet to try and find a solution but everything I've found seems to use a different syntax to me.
Here's my code so far:
#include <iostream>
#include <fstream>
#include <istream>
char in[20];
char out[20];
using namespace std;
struct contact
{
char firstName[14];
char surName[14];
char number[9];
};
//structure definition
int main(void){
cout << "Please enter the input filename: " << endl;
cin >> in;
ifstream input(in);
if(!input){
cerr << "failed to open input file " << in << endl;
exit(1);
}
cout << "Please enter tne output filename: " << endl;
cin >> out;
// read in the input and output filenames
char a;
int b=0;
while (input.good ())
{
a=input.get ();
if (a=='\n')
{
b++;
}
}
// count the number of lines in the input file
input.seekg (0, ios::beg);
//rewind to beginning of file
contact* list = new contact[b];
//dynamically create memory space for array of contacts
int i = 0.;
while(input){
if(i >= b) break;
if(input >> *list[i].firstName >> *list[i].surName >> *list[i].number) i++;
else break;
}
input.close();
//read information from input file into array of contacts
for(int N = 0; N < b; N++){
cout << list[N].firstName << list[N].surName << list[N].number << endl;
}
ofstream output(out);
int k = 0;
for(int k = 0; k<b; k++){
output << list[k].firstName << " " << list[k].surName << " " << list[k].number << endl;
}
//print out the unsorted list to screen and write to output file
//i've done both here just to check, won't print to screen in final version
output.close();
delete []list;
} // end of main()
You reset the files location to the beginning, but the files eofbit is still labeled as true from when you first read the amount of lines. A quick fix to this is re-opening the file after you read the lines, possibly making the line count a function to clean up code.
int lines(const string path)
{
ifstream tmp(path.c_str());
string temp;
int count = 0;
getline(inFile,temp);
while(inFile)
{
count++;
getline(inFile,temp);
}
tmp.close();
return count;
}
Okay, I put together a quick and dirty method using newer C++ constructs to get you most of the way there. You're on your own for writing to the file (trivial) and the quicksort, though I've put the struct into a vector for you, so sorting the vector is as easy as writing a custom function to compare one struct vs the other. I apologize in advance if some of the code is less than canonical C++. I'm way past my bed time, and way tired, but this was interesting enough of a problem that I wanted to give it a go. Happy coding!
#include <iostream>
#include <fstream>
#include <istream>
#include <string>
#include <vector>
#include <algorithm>
#include <cctype>
#include <sstream>
using namespace std;
std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
std::stringstream ss(s);
std::string item;
while(std::getline(ss, item, delim)) {
elems.push_back(item);
}
return elems;
}
std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems;
return split(s, delim, elems);
}
struct contact
{
std::string firstName;
std::string surName;
std::string number;
contact(std::string& fName, std::string& lName, std::string& num) : firstName(fName), surName(lName), number(num) {}
};
//structure definition
char in[20];
char out[20];
int main()
{
std::vector<contact> contacts;
cout << "Please enter the input filename: " << endl;
cin >> in;
ifstream input(in);
if(!input){
cerr << "failed to open input file " << in << endl;
exit(1);
}
cout << "Please enter tne output filename: " << endl;
cin >> out;
std::string sinput;
// read in the input and output filenames
while (input.good ())
{
getline(input, sinput);
vector<string> tokens = split(sinput, ' ');
if (tokens.size() == 3)
{
contact c(tokens[0], tokens[1], tokens[2]);
contacts.push_back(c);
}
}
input.close();
//read information from input file into array of contacts
std::cout << "Outputting from vector..." << std::endl;
for_each(contacts.begin(), contacts.end(), [](contact& c) {
cout << c.firstName << " " << c.surName << " " << c.number << endl;
});
return 0;
}
Also, just want to give credit that the split methods come from this answer on this very site. Cheers!
Related
I a little new to istringstream operations. I need to find a way to iterate over words in a text file containing eight paragraphs. I need to get each words, test for certain conditions, and then store into a linked list if it passes the checklist. All I need help doing is how to extract each word one by one, use it, check it, and so forth. Here is some code I had: Could anyone lend some advice? I have a loop but it doesnt update the value of the string substrings, Thanks
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
using namespace std;
string getLineOfText(ifstream &strIn);
string parseLineOfText(string&);
int main()
{
ifstream in("Text.txt");
istringstream strI;
string substrings;
string strg;
int lineCount = 0;
while (!in.eof())
{
strg = getLineOfText(in);
++lineCount;
strI.str(strg);
while (strI >> substrings)
cout << substrings << " ";
strI.str("");
}
cout << substrings << endl;
cout << endl << lineCount << endl;
system("pause");
return 0;
}
string getLineOfText(ifstream &strIn)
{
string lineTxt;
getline(strIn, lineTxt);
return lineTxt;
}
Move the definition of strI inside the outer while loop.
int main()
{
ifstream in("Text.txt");
string strg;
int lineCount = 0;
while (!in.eof())
{
strg = getLineOfText(in);
++lineCount;
istringstream strI(strg);
string substrings;
while (strI >> substrings)
cout << substrings << " ";
}
cout << endl << lineCount << endl;
system("pause");
return 0;
}
So what I did was this;
ifstream infile("warehouse.txt"); ffile("updated.txt");
ofstream outfile("updated.txt");
basically what I want to do is read from the inputfile 'warehouse.txt' and store contents in an array and then add this array and an extra array to outputfile 'updated.txt'.
Then I want to use 'updated.txt' as an input file as shown in the code above is this allowed, I basically want to store all the data on updated.txt into one big array i.e combine the two arrays, is this allowed? I tried it and my compiler seemed to screw up and I was reading about using vectors instead but am struggling to understand them. thanks.
here is my overall code what I wanted to do was basically take from an input file the 'fruitname' and its corresponding quantity from an input file. store extra entries in an extraarray and then put both these arrays in an output file as stated above and then use that output as an input file so I can aggreagte the data.
THE PROBLEM:
When I try to store from the updated.txt to array my cout's show that I get random numbers in place of what should be the fruitname and its number.
#include <iostream>
#include <string>
#include <cstdlib>
#include <fstream>
using namespace std;
typedef struct items {
string name;
int quantity;
} items_t;
void fileopenchecker (ifstream &FILE);
int readfromfile (ifstream &FILE, items_t fruit[]);
int extrarray (items_t fruit[]);
void writetooutputfile (ofstream &OFILE, items_t fruit[], int size);
void combinearrays (ifstream &final, items_t overallfruit[], int total);
int main()
{
const int MAX_SIZE = 150;
int Nfruit = 0;
int Nextrafruit = 0;
int total;
std::ifstream infile("warehouse.txt");
std::ofstream outfile("updated.txt");
std::ifstream ffile("updated.txt");
items_t extrafruit[MAX_SIZE], fruit[MAX_SIZE], overallfruit[MAX_SIZE];
fileopenchecker(infile);
Nextrafruit = extrarray(extrafruit);
Nfruit = readfromfile(infile, fruit);
total = Nextrafruit + Nfruit;
infile.close();
writetooutputfile(outfile, fruit, Nfruit);
writetooutputfile(outfile, extrafruit, Nextrafruit);
combinearrays (ffile, overallfruit, total);
ffile.close();
return 0;
}
void combinearrays (ifstream &final, items_t overallfruit[], int total){
int i;
for(i=0; i<total; i++){
final >> overallfruit[i].name >> overallfruit[i].quantity;
cout << overallfruit[i].name << overallfruit[i].quantity << endl;
}
}
void fileopenchecker (ifstream &FILE){
if(!FILE.is_open()){
cout << "Your file was NOT detected!" << endl;
exit(1);
}
else{
cout << "Your file was detected" << endl;
}
}
int readfromfile (ifstream &FILE, items_t fruit[]){
int entries = 0;
while(!FILE.eof()){
FILE >> fruit[entries].name >> fruit[entries].quantity;
cout << fruit[entries].name << fruit[entries].quantity << endl;
entries++;
}
return entries;
}
int extrarray (items_t fruit[]){
int runner=1, exentries =0;
while(runner==1){
cout << "Would you like to add entries to your file? (YES-->1 NO-->0)" << endl;
cin >> runner;
if(runner==0){
break;
}
//take the itemname and quantity and stores it in the array.
cout << "Enter the name of the fruit and its quantity" << endl;
cin >> fruit[exentries].name >> fruit[exentries].quantity;
//debugging:
cout << fruit[exentries].name << fruit[exentries].quantity << endl;
exentries++;
}
return exentries;
}
void writetooutputfile (ofstream &OFILE, items_t fruit[], int size){
int entries = 0;
while(entries < size){
cout << fruit[entries].name << fruit[entries].quantity << endl;
OFILE << fruit[entries].name << fruit[entries].quantity << endl;
entries++;
}
}
"I want to do is read from the inputfile 'warehouse.txt'"
{
std::ifstream ifs("warehouse.txt");
// reading from ifs ...
... "to outputfile 'updated.txt'"
std::ofstream ofs("updated.txt");
// writing to ofs ...
}
... "Then I want to use 'updated.txt' as an input file" ~> create another instance of ifstream:
{
std::ifstream ifs2("updated.txt");
// reading from ifs2 ...
}
Yes, and it's possible to to use just two file streams if you use std::fstream. For example:
#include <fstream>
#include <iostream>
#include <string>
int main(void)
{
std::ifstream infile("a.txt");
// create file for both reading and writing
std::fstream ffile("b.txt", std::fstream::in | std::fstream::out | std::fstream::trunc);
// read contents of file a and write to file b
std::string line;
while (std::getline(infile, line))
{
std::cout << line << std::endl;
ffile << line << std::endl;
}
// flush the output to disk
ffile.flush();
// go back to the start of the output file before reading from it
ffile.seekg(0);
// read contents of output file again.
while (std::getline(ffile, line))
{
std::cout << line << std::endl;
}
return 0;
}
The problem is that file won't be read...Apparently there's a problem with an array but I don't really know how fix this issue...I'm a beginner to C++ 'arrays' and 'strings'...
My file should read the code, then translate the file, then output the text into a new file..
#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
#include <fstream>
#include <math.h>
#include <stdio.h>
#include <string>
#include <string.h>
using namespace std;
int main()
{
// Declarations
string reply;
string inputFileName;
ifstream inputFile;
ofstream outFile;
char character;
cout << "Input file name: ";
getline(cin, inputFileName);
// Open the input file.
inputFile.open(inputFileName.c_str());
// Check the file opened successfully.
if ( ! inputFile.is_open()) {
cout << "Unable to open input file." << endl;
cout << "Press enter to continue...";
getline(cin, reply);
return 1;
}
// This section reads and echo's the file one character (byte) at a time.
while (inputFile.peek() != EOF) {
inputFile.get(character);
//cout << character;
//Don't display the file...
char cipher[sizeof(character)];
//Caesar Cipher code...
int shift;
do {
cout << "enter a value between 1-26 to encrypt the text: ";
cin >> shift;
}
while ((shift <1) || (shift >26));
int size = strlen(character);
int i=0;
for(i=0; i<size; i++)
{
cipher[i] = character[i];
if (islower(cipher[i])) {
cipher[i] = (cipher[i]-'a'+shift)%26+'a';
}
else if (isupper(cipher[i])) {
cipher[i] = (cipher[i]-'A'+shift)%26+'A';
}
}
cipher[size] = '\0';
cout << cipher << endl;
}
cout << "\nEnd of file reached\n" << endl;
// Close the input file stream
inputFile.close();
cout << "Press enter to continue...";
getline(cin, reply);
return 0;
}
To make it short: You're on c++ so just don't use the whole C stuff.
Don't use character arrays, use std::string
Don't use islower(char) but use std::islower(char,locale)
Don't use C-style arrays but std::array (compile time constant size) or std::vector (dynamic size)
You'll want to have it more like this:
#include <string>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <locale>
int main (void)
{
std::string input_filename;
std::cout << "Input file name: ";
std::getline(std::cin, input_filename);
unsigned int shift;
do
{
std::cout << "Enter a value between 1-26 to encrypt the text: ";
std::cin >> shift;
}
while ((shift == 0) || (shift > 26));
try
{
std::string filestring;
std::ifstream input(input_filename, std::ios_base::in);
if (input)
{
input.seekg(0, std::ios::end);
filestring.reserve(input.tellg());
input.seekg(0, std::ios::beg);
filestring.assign
(std::istreambuf_iterator<char>(input),
std::istreambuf_iterator<char>());
}
else
{
std::string error_string("Reading failed for: \"");
error_string.append(input_filename);
error_string.append("\"");
throw std::runtime_error(error_string);
}
std::string result;
result.reserve(filestring.size());
std::locale const loc;
for (auto character : filestring)
{
char const shifter(std::islower(character, loc) ? 'a' : 'A');
result.push_back((character-shifter+shift)%26+shifter);
}
std::cout << result << std::endl;
}
catch (std::exception & e)
{
std::cout << "Execution failed with an exception: " << std::endl;
std::cout << e.what() << std::endl;
}
}
This solution requires C++11 support. If you do not have C++11 you can replace the loop with:
size_t const N(filestring.length());
for (size_t i(0u); i<N; ++i)
{
char const shifter(std::islower(filestring[i], loc) ? 'a' : 'A');
result.push_back((filestring[i]-shifter+shift)%26+shifter);
}
From looking at your code, "character" is declared as a char which means it can only store one byte of information. Yet later on you start using it as if it was an array of characters.
You are also declare "cipher" as a char array that you manual manage like a string which is error prone. The real issue however is that you're mixing C-like code in C++. In other words, the way your code is written isn't considered idiomatic C++.
Pixelchemist already went over the important points so I'll just present a minimal refactored working example of your above code:
#include <iostream>
#include <string>
#include <fstream>
#include <stdlib.h>
using namespace std;
int main()
{
string filename;
cout << "enter input file: ";
cin >> filename;
ifstream inputFile( filename.c_str() );
string plaintext;
do
{
plaintext += inputFile.get();
}while(inputFile);
cout << plaintext << endl;
string &ciphertext = plaintext;
//Caesar Cipher code...
int shift = rand() % 26 + 1;
for(size_t i = 0; i < ciphertext.size(); ++i)
{
if (islower(ciphertext[i])) {
ciphertext[i] = (ciphertext[i] - 'a' + shift) % 26 + 'a';
}
else if (isupper(ciphertext[i])) {
ciphertext[i] = (ciphertext[i] - 'A' + shift) % 26 + 'A';
}
}
cout << ciphertext << endl;
}
You'll notice in the refactor that I've done away with char and char[] arrays altogether and replaced it with std::string. I'm also performing the cipher operation inplace on the plaintext input. This is done by making a reference alias to plaintext called ciphertext for readability. Also in my example, the shift is done randomly for prototyping but you should change it to take it as a user input instead.
You are working with a single char, thats e.g. just one letter or a number. So the whole thing with size handling is useless, because the size is always 1. You probably should use const char*. But then you can't use filestream.get() at all, because it only returns a single char (not and cstring aka const char*).
And you can use fstream.get() as condition for the loop, so you don't need to ask for the eof flag.
char my_char;
std::ifstream infstream("filename.txt");
if(!infstream.isopen())
return -1;
while(infstream.get(my_char) {
//do some stuff
}
or
std::string my_string;
std::ifstream infstream("filename.txt");
if(!infstream.isopen())
return -1;
while(infstream >> my_string) {
//do some stuff
}
for dynamic arrays in C++ use std::vector or std::list or ... one of the other STL containers, so you don't have to waste your time on memory management and using static sized arrays.
And std::string is the way to go for strings in C++. It is something similar to the STL containers, but just for char's.
I need to read data from a text file, and insert the data in an array of struct. The data file is in the following format:
productname price quantity
my main concern is to read the product name, which consist of one, and two words. Should I approach product name as a c-string or as a string literal?
any help appreciated
#include <iostream>
#include <fstream>
using namespace std;
const int SIZE = 15; //drink name char size
const int ITEMS = 5; //number of products
struct drinks
{
char drinkName[SIZE];
float drinkPrice;
int drinkQuantity;
};
int main()
{
//array to store drinks
drinks softDrinks[ITEMS];
//opening file
ifstream inFile;
inFile.open("drinks.txt");
char ch;
int count = 0; //while loop counter
if(inFile)
{
while(inFile.get(ch))
{
//if(isalpha(ch)) { softDrinks[count].drinkName += ch; }
//if(isdigit(ch)) { softDrinks[count].drinkPrice += ch; }
cout << ch;
}
cout << endl;
count++;
}
else
{
cout << "Error opening file!\n";
system("pause");
exit(0);
}
system("pause");
return 0;
}
Since you ask for "any help", here's my view: Forget everything you wrote, and use C++:
#include <fstream> // for std::ifstream
#include <sstream> // for std::istringstream
#include <string> // for std::string and std::getline
int main()
{
std::ifstream infile("thefile.txt");
std::string line;
while (std::getline(infile, line))
{
std::istringstream iss(line);
std::string name;
double price;
int qty;
if (iss >> name >> price >> qty)
{
std::cout << "Product '" << name << "': " << qty << " units, " << price << " each.\n";
}
else
{
// error processing that line
}
}
}
You could store each line of data in a std::tuple<std::string, int, double>, for example, and then put those into a std::vector as you go along.
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