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::string and the “price” as a
double````. 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. . .
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.