简体   繁体   English

c++ - 如何从包含空格的文件中读取字符串

[英]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). alex booth,15,1 (全名,带空格,年龄,身份证)。

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.例如,我想在文件中搜索诸如“alexbooth”之类的名称,但似乎搜索无法找到该词,尽管该词可用。 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.当您从文件中读取行时,您根本没有考虑逗号,然后operator>>在您的内部while循环中失败。

Also, your outer while loop is wrong , too.此外,您的外部while循环也是错误的

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.我想提出一个额外的解决方案,一个“更”现代的 C++ 和面向对象的解决方案。 And, a full working solution.而且,一个完整的工作解决方案。

In C++ we usually put data, and functions operating on that data, into an object, a class.在 C++ 中,我们通常将数据和对数据进行操作的函数放入一个对象、一个类中。 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.因此,在您的示例中,“id”、“name”和“price”是对象的属性,从std::istream提取它们或将它们插入std::ostream应仅由该对象知道并由它。

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.因此,我们创建了一个类/结构体,定义了 3 个数据成员,即“id”、“name”和“price”为unsigned long", std::string and the “price” as a double``` `. 然后,我们覆盖提取器运算符 (>>) 和插入器运算符。提取器运算符有点棘手,因为我们首先读取完整行,然后将其拆分为标记。

In C++ your file structure is called CSV (Comma Separated Value).在 C++ 中,您的文件结构称为 CSV(逗号分隔值)。 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 . “拆分”也称为“标记化”,C++ 具有用于此目的的专用功能: std::sregex_token_iterator This thing is an iterator.这个东西是一个迭代器。 For iterating over a string, hence “sregex”.用于迭代字符串,因此是“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.开始部分定义了我们将操作的输入范围,然后有一个 std::regex 用于在输入字符串中应该匹配/或不应该匹配的内容。 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.我们可以使用这个迭代器将标记存储在 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. std::vector 有一个范围构造函数,它接受 2 个迭代器作为参数,并将第一个迭代器和第二个迭代器之间的数据复制到 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 .定义了一个变量“令牌”作为std::vector ,并且使用的所谓的范围-构造std::vector Please note: I am using C++17 and can define the std::vector without template argument.请注意:我使用的是 C++17,并且可以在没有模板参数的情况下定义std::vector The compiler can deduce the argument from the given function parameters.编译器可以从给定的函数参数中推导出参数。 This feature is called CTAD ("class template argument deduction").此功能称为 CTAD(“类模板参数推导”)。

Additionally, you can see that I do not use the "end()"-iterator explicitly.此外,您可以看到我没有明确使用“end()”-迭代器。

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.该迭代器将从具有正确类型的空的大括号括起来的初始化列表中构造,因为由于std::vector构造函数要求,它将被推导出与第一个参数的类型相同。

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”.然后,在读取该行并将其拆分为标记后,我们检查是否正好有 3 个标记,然后将结果存储为“id”、“name”和“price”。 So, overall, a very simple operation.所以,总的来说,一个非常简单的操作。

in main I put some example driver code.main我放了一些示例驱动程序代码。 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:读取完整的 CSV 文件并解析它,是通过一个简单的单行代码完成的:

std::vector productList(std::istream_iterator<Product>(productFile), {});

Finding an element is just using std::find_if .查找元素只是使用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.当然还有很多其他的解决方案。 . . . .

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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