简体   繁体   中英

How to read a string from a file with space within it c++

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM