I have created a .txt file which a sample would be like this:
Item 1 $1.00
Item # $2.00
Item & $3.50
Item ( $0.30
If I want to ask the user to input the item they want and find the price, how can I do that?
I have tried using .find() to solve the problem, but it didn't work because array cannot use .find().
I'm new to C++, please explain.
#include <array>
#include <string>
#include <fstream>
using namespace std;
string product;
ifstream file("Item.txt");
int main(){
cout << "Enter item you want" << endl;
cin >> product;
file.open("Item.txt", fstream::ate);
findItem.find((file.begin(),file.end(), product) + 1);
cout << findItem << endl;
file.close();
}
Here's a sample code fragment to search your file:
std::string text_line;
while (std::getline(file, text_line))
{
std::string text_item;
std::string item_name;
char dollar_sign;
double price;
std::istringstream text_stream(text_line);
text_stream >> text_item >> item_name >> dollar_sign >> price;
if (product == item_name)
{
std::cout << "Found item\n";
break;
}
}
The usual method is to create a class that models the input line, then overload operator>> to read from a stream.
Notes: 1) commas separating fields would make reading easier; 2) Remove the dollar sign to make reading easier.
Related
Question may seem like a duplicate but I have researched and found nothing that would actually answer my question.
So I have a task that let's the user input groceries as a string, the weight and price as doubles. Simple enough, right? Well, the problem is that the user will enter it in the following format:
Enter product 1: Potato; 5 kg; 49 kr;
Enter product 2: Carrot; 0.5 kg; 13 kr;
Enter product 3: Onion; 0.1 kg; 2 kr;
And then the products will be sorted in a specific manner. However that is not what I need help with, I have a problem with reading in only "Potato", "5" and "49" and store them in seperate variables.
#include <iostream>
#include <iomanip>
#include <vector>
#include <algorithm>
using namespace std;
struct Product_Type
{
string name;
double weight;
double price;
};
void get (Product_Type & p)
{
cin >> p.name;
cin.ignore(1);
cin >> p.weight;
cin.ignore(2);
cin >> p.price;
cin.ignore(2);
}
If I do it like this, I will store "Potato;" and not "Potato" in my name variable. So my question is how do I manipulate a string to only read to a certain index and store the content in a variable? I don't want to read the actual semi-colons. Is there a simple way to do this?
You need to understand how formatted input and unformatted input works.
Please read here about it.
You do not need ignore at all. You can do all with simple statements.
But the precondition is that the input matches your specification. Any input of wrong data leads to a problem. So, let's assume that the input follows the above pattern. OK.
Regarding your problem, where you read "potato;" instead of "potato". This is because you use a formatted input function >> to read a string. And this function will stop, if it sees a white space and not before. It will also ignore leading white space by default, but that is not important here.
To solve the problem to read a string until a certain delimiter, you can use the unformatted input function std::getline. Please see here.
So, to get the name, you may write std::getline(std::cin, p.name, ';');. This will read the name from the input stream, including the semicolon, but store the pure name, without the semicolon in the string.
Next we are talking about reading 5 kg; 49 kr. We want to have the "weight". We can simply use formatted input function >> to get it. There is a white space after that, so, very simple. You can write std::cin >> p.weight; to get it.
Next we have to read kg; 49 kr. So, we will use a dummy string and with that can read the "kg;" which we do simple not use. So, with a dummy string "s1", we can write std::cin >> s1;. The kg; will be gone from the stream.
Now we see 49 kr;. Reading the price is again simple using >>. We will write std::cin >> p.price; and have it.
Last but not least we need to read the rest of the line, including the enter. For that we use again std::getline. No need to specify any delimiter, because the standard delimiter is already '\n'.
Then the full code looks like the below:
#include <iostream>
#include <string>
struct Product_Type
{
std::string name;
double weight;
double price;
};
void get(Product_Type& p)
{
std::string s1, s2;
std::getline(std::cin, p.name, ';');
std::cin >> p.weight;
std::cin >> s1;
std::cin >> p.price;
std::getline(std::cin, s2);
}
int main() {
Product_Type pt;
get(pt);
std::cout << pt.name << " --> " << pt.weight << " --> " << pt.price << '\n';
return 0;
}
But this is not considered to be good. There may be errors in the input and with that the failbit of std::cin would be set and you cannot continue to read.
Therefore, normally, we would read first the complete line with std::getline which will most likely always work. Then, we "convert" the string to a stream using an std::istringstream to be able to extract the data from there. If the complete line has an error, then only the temporary std::istringstream will be gone. So this approach is a little bit more robust.
This could then look like:
#include <iostream>
#include <string>
#include <sstream>
struct Product_Type
{
std::string name;
double weight;
double price;
};
void get(Product_Type& p)
{
std::string line, s1, s2;
std::getline(std::cin, line);
std::istringstream iss(line);
std::getline(iss, p.name, ';');
iss >> p.weight;
iss >> s1;
iss >> p.price;
std::getline(iss, s2);
if (not iss)
std::cerr << "\nError: Problem with input\n\n";
}
int main() {
Product_Type pt;
get(pt);
std::cout << pt.name << " --> " << pt.weight << " --> " << pt.price << '\n';
return 0;
}
And a little bit more advanced with chaining the io-functions, we can write
#include <iostream>
#include <string>
#include <sstream>
struct Product_Type
{
std::string name;
double weight;
double price;
};
void get(Product_Type& p)
{
std::string line, s1, s2;
std::getline(std::cin, line);
std::istringstream iss(line);
std::getline(std::getline(iss, p.name, ';') >> p.weight >> s1 >> p.price, s2);
if (not iss)
std::cerr << "\nError: Problem with input\n\n";
}
int main() {
Product_Type pt;
get(pt);
std::cout << pt.name << " --> " << pt.weight << " --> " << pt.price << '\n';
return 0;
}
All the above is very simplified, but may give you a starting point for better ideas.
So, I am trying to implement a function that can read a file and save some variables to it. The text file will look like this
- Account Number: 12345678
- Current Balance: $875.00
- Game Played: 2
- Total Amount Won: $125.00
- Total Amount Loss: $250.00
So far, I am able to read the account number but when I try to read the rest using the same method, nothing gets outputted to the console.
Here is my progress so far.
#include <fstream>
#include <string>
using namespace std;
int main()
{
ifstream inFile;
int accNum;
string accID;
int games;
double balance;
double amountWon, amountLost;
// Check if file with account number exists
cout << "Please enter account number: ";
cin >> accNum;
// Append correct format to string
accID.append("acc_");
accID.append(to_string(accNum));
accID.append(".txt");
// Open the file
inFile.open(accID);
// Check if the account number exists
if (!inFile)
{
cout << "Account number does not exist.\n";
}
int num;
string str, str2, str3;
// Read through the file
while (inFile >> str >> str2 >> str3 >> num)
{
cout << num << " ";
}
return 0;
}
When I run this code, I can get the account number to be output, but when I try adding more variables to read through the rest of the file nothing gets output to console.
You can use std::getline to read the rest of the file as shown below:
#include <fstream>
#include <string>
#include <iostream>
#include <sstream>
using namespace std;
int main()
{
int accNum;
string accID;
int games;
double balance;
double amountWon, amountLost;
// Check if file with account number exists
cout << "Please enter account number: ";
cin >> accNum;
// Append correct format to string
accID.append("acc_");
accID.append(to_string(accNum));
accID.append(".txt");
std::cout<<accID<<std::endl;
// Open the file
ifstream inFile(accID);
if(inFile)
{
std::string strDash,strTitle, strNumber;
int num;
// Read through the file
while (std::getline(inFile, strDash, ' '),//read the symbol -
std::getline(inFile, strTitle, ':'), //read the title(for eg, Account number,Current Balance etc)
std::getline(inFile, strNumber, '\n')) //read the number at the end as string
{
std::cout<<strDash<<" "<<strTitle<<strNumber<<std::endl;//print everything read
}
}
// Check if the account number exists
else
{
cout << "Account number does not exist.\n";
}
return 0;
}
The output of the program can be seen here.
i wrote a code for searching a string in a file. Actually, i put my data in my file like this:
alex booth,15,1 (fullname with space, age, id).
There isn't any problem with saving data in the file, i got a big problem in searching for a string in that file.
For example, i want to search in the file for a name like "alex booth", but it seems the search can't find that the word despite of being available. I can guess that there's a problem with saving the name with space, but i don't know what should i do for fixing it. As the matter of fact, i don't know exactly how can we search for a name with blank space within it.
Here is my code:
using namespace std;
struct product{
string name;
int price, id;
};
int main() {
product myProduct;
ofstream productList("product.csv", ios::app | ios::out);
getline(cin, myProduct.name);
cin >> myProduct.price;
cin >> myProduct.id;
productList << myProduct.name << ',' << myProduct.price << ',' << myProduct.id << endl;
productList.close();
cout << "----" << endl;
system("pause");
ifstream checkList("product.csv");
string dummy;
string check;
cin.ignore();
getline(cin, check);
while (!checkList.eof()) {
while (checkList >> myProduct.name >> myProduct.price >> myProduct.id) {
if (myProduct.name == check) {
cout << "Product Name: " << myProduct.name <<
" - Product's Price: " << myProduct.price <<
" - ID Number: " << myProduct.id << endl;
}
}
}
checkList.close();
return 0;
}
When you are reading lines from the file, you are not taking the commas into account at all, then operator>> fails inside your inner while loop.
Also, your outer while loop is wrong, too.
Try something more like this instead:
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <limits>
#include <cstdlib>
using namespace std;
struct product{
string name;
int price, id;
};
int main() {
product myProduct;
cout << "Enter product name: ";
getline(cin, myProduct.name);
cout << "Enter product price: ";
cin >> myProduct.price;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "Enter product id: ";
cin >> myProduct.id;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
ofstream productList("product.csv", ios::app);
productList << myProduct.name << ',' << myProduct.price << ',' << myProduct.id << std::endl;
productList.close();
cout << "----" << endl;
system("pause");
cin.ignore();
cout << "Enter name to look for: ";
string check;
getline(cin, check);
ifstream checkList("product.csv");
string line;
char comma;
while (getline(checkList, line)) {
istringstream iss(line);
if (getline(iss, myProduct.name, ',') && (iss >> myProduct.price >> comma >> myProduct.id) && (comma == ',')) {
if (myProduct.name == check) {
cout << "Product Name: " << myProduct.name <<
" - Product's Price: " << myProduct.price <<
" - ID Number: " << myProduct.id << endl;
}
}
}
checkList.close();
return 0;
}
Alternatively:
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <limits>
#include <cstdlib>
using namespace std;
struct product{
string name;
int price, id;
};
istream& getInt(istream &in, int &value)
{
string str;
if (getline(in, str, ',')) {
try {
value = stoi(str);
}
catch (...) {
in.setstate(ios::failbit);
}
}
return in;
}
int main() {
product myProduct;
cout << "Enter product name: ";
getline(cin, myProduct.name);
cout << "Enter product price: ";
cin >> myProduct.price;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "Enter product id: ";
cin >> myProduct.id;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
ofstream productList("product.csv", ios::app);
productList << myProduct.name << ',' << myProduct.price << ',' << myProduct.id << std::endl;
productList.close();
cout << "----" << endl;
system("pause");
cin.ignore();
cout << "Enter name to look for: ";
string check;
getline(cin, check);
ifstream checkList("product.csv");
string line;
while (getline(checkList, line)) {
istringstream iss(line);
if (getline(iss, myProduct.name, ',') && getInt(iss, myProduct.price) && getInt(iss, myProduct.id)) {
if (myProduct.name == check) {
cout << "Product Name: " << myProduct.name <<
" - Product's Price: " << myProduct.price <<
" - ID Number: " << myProduct.id << endl;
}
}
}
checkList.close();
return 0;
}
I would like to propose an additional solution, a “more” modern C++ and object oriented solution. And, a full working solution.
In C++ we usually put data, and functions operating on that data, into an object, a class. The class and only the class should know how to handle its data.
So in your example the “id,” "name" and “price” are attributes for an object, and extracting them from a std::istream or inserting them into a std::ostream should be only known by that object and handled by it.
As a consequence, we create a class/struct, define 3 data members, namely the "id", “name” and "price" as a unsigned long",std::stringand the “price” as adouble````. Then, we overwrite the extractor operator (>>) and the inserter operator. The extractor operator is a little bit tricky, since we first read a complete line and then split it into tokens.
In C++ your file structure is called CSV (Comma Separated Value). And, reading this is a standard task. First, read the complete line, then, using a given delimiter, extract the tokens of that string. The “splitting” is also called “tokenizing” and C++ has a dedicated functionality for that purpose: std::sregex_token_iterator.
This thing is an iterator. For iterating over a string, hence “sregex”. The begin part defines, on what range of input we shall operate, then there is a std::regex for what should be matched / or what should not be matched in the input string. The type of matching strategy is given with last parameter.
1 --> give me the stuff that I defined in the regex and
-1 --> give me that what is NOT matched based on the regex.
We can use this iterator for storing the tokens in a std::vector. The std::vector has a range constructor, which takes 2 iterators as parameter, and copies the data between the first iterator and 2nd iterator to the std::vector. The statement
std::vector tokens(std::sregex_token_iterator(line.begin(), line.end(), re, -1), {});
defines a variable “tokens” as a std::vector and uses the so called range-constructor of the std::vector. Please note: I am using C++17 and can define the std::vector without template argument. The compiler can deduce the argument from the given function parameters. This feature is called CTAD ("class template argument deduction").
Additionally, you can see that I do not use the "end()"-iterator explicitly.
This iterator will be constructed from the empty brace-enclosed initializer list with the correct type, because it will be deduced to be the same as the type of the first argument due to the std::vector constructor requiring that.
Then, after reading the line and splitting it into tokens, we check, if we have exactly 3 tokens and then store the result as "id", “name” and “price”. So, overall, a very simple operation.
in main I put some example driver code. For example, you can see that I store a new product into the file with a simple
productFile << product;
statement. And reading the complete CSV file and parsint it, is done with a simple one-liner:
std::vector productList(std::istream_iterator<Product>(productFile), {});
Finding an element is just using std::find_if.
Please see below a complete example:
(I put many comments and empty lines to make the code more readble.)
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
#include <fstream>
#include <regex>
const std::string delimiter(",");
const std::regex re(delimiter.c_str());
struct Product {
// Data
unsigned long id{};
std::string name{};
double price{};
// Member Functions
// Simple Inserter.
friend std::ostream& operator << (std::ostream& os, const Product& product) {
// Unfortunately the ostream_joiner does not work on my system . . .
return os << product.id << delimiter << product.name << delimiter << product.price << "\n";
}
// simple Extractor
friend std::istream& operator >> (std::istream& is, Product& product) {
// Read a complete line
if (std::string line{}; std::getline(is,line)) {
// Convert line into tokens. Split any number of csv columns. One-liner
std::vector tokens(std::sregex_token_iterator(line.begin(), line.end(), re, -1), {});
// if we have read 3 tokens as expected
if (3 == tokens.size()) {
// Assign values to data member
product.id = std::strtoul(static_cast<std::string>(tokens[0]).c_str(), nullptr, 10);
product.name = tokens[1];
product.price = std::strtod(static_cast<std::string>(tokens[2]).c_str(), nullptr);
}
}
return is;
}
};
int main() {
const std::string filename{ "r:\\product.csv" };
// First, we ask the user to enter product data
std::cout << "\nEnter product ID, product name and product price in one line separated by comma:\n";
// Get input
if (Product product; std::cin >> product) {
// Save the user data in a file
if (std::ofstream productFile(filename,std::ios::app); productFile) {
// Write to output file stream
productFile << product;
}
else {
std::cerr << "\n\n***Could not write to file\n\n";
}
}
// Now test the search function
// Get the name to look for
std::cout << "\n\nEnter a name to look for:\n ";
if (std::string name{}; std::getline(std::cin, name)) {
// Open the file for reading
if (std::ifstream productFile(filename); productFile) {
// Read the complete file. One-liner
std::vector productList(std::istream_iterator<Product>(productFile), {});
// Search for the name
auto result = std::find_if(productList.begin(), productList.end(), [&name](const Product& p) { return p.name == name; });
// If found, print result
if (result != productList.end())
std::cout << "Found: " << *result <<"\n\n";
else
std::cout << "Name '" << name <<"' not found\n\n";
// For the fun of it: Show all data
std::copy(productList.begin(), productList.end(), std::ostream_iterator<Product>(std::cout));
}
else {
std::cerr << "\n\n***Could not read from file\n\n";
}
}
return 0;
}
I hope that gives you an idea, how such a problem can be solved. Of course there are tons of other solutions. . .
I need to make a Point of Sale software which is reading product names, barcodes and prices from a given text file. I can extract all the required data from the file, but I don't know how to use that data. To be precise I need to calculate the prices of picked products. My program is currently able to ask a user for a barcode and print out the chosen products
#include <iostream>
#include <string>
#include <fstream>
#include <cstdlib>
using namespace std;
void KeyWord(ifstream &FileSearch)
{
string line;
string letters[5];
ifstream readSearch;
cout<< "Enter a barcode of a product: \n";
cin >> letters[0];
cin >> letters[1];
cin >> letters[2];
cin >> letters[3];
cin >> letters[4];
readSearch.open("Products.txt");
if(readSearch.is_open())
{
while (getline(readSearch, line))
{
while (line.find(letters[0])!=string::npos || line.find(letters[1])!=string::npos || line.find(letters[2])!=string::npos || line.find(letters[3])!=string::npos || line.find(letters[4])!=string::npos)
{
cout << line << "\n";
break;
}
}
}
}
int main()
{
ifstream file("Products.txt");
KeyWord(file);
return 0;
}
If each line contains a set of product names, barcodes and prices, get the line into an std::stringstream and move on from there.
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;
}
}