简体   繁体   English

难以从文件中读取

[英]Difficulty reading from a file

I have a file with comma separated values我有一个包含逗号分隔值的文件

M,21,Hazel
F,49,Stephen

I am sending ifstream into a function that takes in istream to read the line.我将 ifstream 发送到一个 function 中,它接受 istream 来读取该行。

ifstream file(fileName);
char gender;
file.get(gender);
file.ignore();  // ignore comma

if (gender == 'M') {
  Gender* tmp = new Male;
  file >> *tmp;
} else if (gender == 'F') {
  Gender* tmp = new Female;
  file >> *tmp;
}

The first character up to the comma is read correctly, but when I send it to read it asks for user input when it is not wanted.逗号之前的第一个字符被正确读取,但是当我发送它来阅读时,它会在不需要时要求用户输入。 It doesnt read the rest of the file ie "49,Stephen"它不读取文件的 rest 即“49,斯蒂芬”

istream& operator>>(istream& istr, ReadW& ref) {
  return ref.read(istr);
}

istream& read(istream& is) {
  char tName[16];
  is >> age;
  is.ignore();  // ignore comma
  is.getline(tName, 16, ',');
}

Basically you are asking about reading a csv file and splitting it into tokens.基本上,您是在询问有关读取 csv 文件并将其拆分为令牌的信息。 If you search for this here on SO, you will find many many posts which explain how to do.如果你在 SO 上搜索这个,你会发现很多解释如何做的帖子。

But in your case it maybe simpler.但就您而言,它可能更简单。 If it is guaranteed that the source file has exactly the format abouve, without additional spaces before or after the comma, then you can use the standard extractor mechanisms for streams.如果保证源文件的格式完全符合上述格式,逗号前后没有额外的空格,那么您可以使用流的标准提取器机制。 You just will read the comma into a dummy variable.您只需将逗号读入虚拟变量即可。 This may look like:这可能看起来像:

// What we want to read
char gender{};
unsigned int age{};
std::string name{};
// This is a dummy
char comma{};

while (testDataStream >> gender >> comma >> age >> comma >> name) {

This will read the first character int the variable gender and then read the comma and put it into the comma variable.这将读取变量gender中的第一个字符,然后读取逗号并将其放入comma变量中。 This we will simply not use.我们根本不会使用这个。 Then, we will continue to extract more values in the same way.然后,我们将继续以相同的方式提取更多的值。

If the structure of the source file is different, it will not work.如果源文件的结构不同,它将不起作用。 For example, if the first variable would be a std::string , then testDataStream >> someString would read the comlplete line up to the next white space.例如,如果第一个变量是std::string ,那么testDataStream >> someString将读取完整行直到下一个空格。 But, with the existing structure it will work.但是,在现有结构下它会起作用。

Anyway, you can use also a different functionality whi,ch will give you a little bit more security in case of malformed input lines.无论如何,您还可以使用不同的功能,在输入行格式错误的情况下,它会为您提供更多的安全性。 And that is, first read a complete line, then put it into a std::istringstream and extract the data from that.也就是说,首先读取完整的一行,然后将其放入std::istringstream并从中提取数据。

If you would have complete different data structure, then you would probaly use the approach with std::getline with delimiter or the std::regex_token_iterator .如果你有完全不同的数据结构,那么你可能会使用带有分隔符的std::getlinestd::regex_token_iterator的方法。 But this would be to much for here.但这对这里来说就太多了。

Additionally, you obviously have a class hierachy.此外,您显然有一个 class 层次结构。 And you create derived classes based on a value read at runtime.并且您根据运行时读取的值创建派生类。 This is usually solved with the Abstract Factory pattern.这通常用抽象工厂模式来解决。

I created a workable example, where you can see all these mechanisms.我创建了一个可行的示例,您可以在其中看到所有这些机制。 Please note: I will not use Plain C-Style char arrays for strings.请注意:我不会将 Plain C-Style char arrays 用于字符串。 And, I will never use raw pointers for owned memory. For that smart pointers shall be used.而且,我永远不会对拥有的 memory 使用原始指针。为此,应该使用智能指针。

#include <iostream>
#include <sstream>
#include <memory>
#include <map>
#include <functional>

std::istringstream testDataStream(R"(F,21,Hazel
M,49,Stephen)");

// Abstract base class "Person"
struct Person {

    // Constructor
    Person(const unsigned int a, const std::string& n) : age(a), name(n) {}

    // Do not forget the virtual destructor
    virtual ~Person() { std::cout << "Destructor Base\n\n\n"; }

    // The data: name and age
    unsigned int age{};
    std::string name{};

    // Since the inserter is not none member function, we will write
    // a separate virtual function to do the job polymorph
    virtual std::ostream& output(std::ostream& os) const = 0;

    // Inserter, will call our internal output function
    friend std::ostream& operator << (std::ostream& os, const Person& p) {
        return p.output(os);
    }
};

// Derived class for a male Person
struct Male : public Person {
    // Constructor
    Male(const unsigned int age, const std::string& name) : Person(age, name) {}
    virtual ~Male() { std::cout << "Destructor Male\n"; }

    // And output
    virtual std::ostream& output(std::ostream& os) const override {
        return os << "Male Person:\nAge:\t" << age << "\nName:\t" << name << '\n';
    }
};

// Derived class for a female Person
struct Female : public Person {

    // Constructor
    Female(const unsigned int age, const std::string& name) : Person(age, name) {}
    virtual ~Female() { std::cout << "Destructor Female\n"; }

    // And output
    virtual std::ostream& output(std::ostream& os) const override {
        return os << "Female Person:\nAge:\t" << age << "\nName:\t" << name << '\n';
    }
};

// "Creation" Functions for abstract factory
std::unique_ptr<Person> createMale(const unsigned int age, const std::string& name) { return std::make_unique<Male>(age, name); }
std::unique_ptr<Person> createFemale(const unsigned int age, const std::string& name) { return std::make_unique<Female>(age, name); }

// Abstract factory
struct AbstractFactory {
    // Abbreviation for finding
    using Map = std::map<char, std::function<std::unique_ptr<Person>(const unsigned int, const std::string&)>>;
    Map creationFunctions{
        {'F', createFemale },
        {'M', createMale }
    };
    std::unique_ptr<Person> create(const char selector, const unsigned int age, const std::string& name) {
        // If the selector is in the map
        if (Map::iterator searchResult{ creationFunctions.find(selector) }; searchResult != creationFunctions.end())
            // Then we call the corresponding creation function
            return creationFunctions[selector](age, name);
        else
            // No key found, the return nullptr (Empty Person());
            return std::unique_ptr<Person>();
    }
};
// Our abstract factor
AbstractFactory abstractFactory{};

// Driver code
int main() {

    // What we want to read
    char gender{};
    unsigned int age{};
    std::string name{};
    // This is a dummy
    char comma{};
    std::string line{};

//#define DIRECT_READ 
#ifdef DIRECT_READ

    // As long as there is data in the stream, read the gender and the comma
    while (testDataStream >> gender >> comma >> age >> comma >> name) {

#else

    while (std::getline(testDataStream, line)) {
        std::istringstream iss{ line };
        if (iss >> gender >> comma >> age >> comma >> name) {

#endif

            // Create a Person, either male or female
            std::unique_ptr<Person> person = abstractFactory.create(gender, age, name);

            // Polymorphism. Call the adequate member function
            std::cout << *person << '\n';

            // Do other stuff 
            // . . . 

        }
#ifndef DIRECT_READ
    }
#endif
    return 0;
}

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

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