Как читать поток с разделенными запятыми значениями в C ++? - PullRequest
1 голос
/ 17 марта 2020

Я хотел бы подчеркнуть основной вопрос ниже:

Предположим, у вас есть файл CSV и заполните ячейки заголовками ввода

Код должен прочитать это из файла .csv и записать результаты в файл .csv. Также полезно кодировать вывод общего числа дел плюс среднее число дел. Вот примерная форма SO, и я хотел бы увидеть, как это можно эффективно использовать для завершения этого базового примера.

   void create() 
{ 
    // file pointer 
    fstream fout; 

    // opens an existing csv file or creates a new file. 
    fout.open("reportcard.csv", ios::out | ios::app); 

    cout << "Enter the details of 5 students:"
        << " roll name maths phy chem bio"; 
    << endl; 

    int i, roll, phy, chem, math, bio; 
    string name; 

    // Read the input 
    for (i = 0; i < 5; i++) { 

        cin >> roll 
            >> name 
            >> math 
            >> phy 
            >> chem 
            >> bio; 

        // Insert the data to file 
        fout << roll << ", "
            << name << ", "
            << math << ", "
            << phy << ", "
            << chem << ", "
            << bio 
            << "\n"; 
    } 
} 

Также, прочитайте конкретную запись

void read_record() 
{ 

    // File pointer 
    fstream fin; 

    // Open an existing file 
    fin.open("reportcard.csv", ios::in); 

    // Get the roll number 
    // of which the data is required 
    int rollnum, roll2, count = 0; 
    cout << "Enter the roll number "
        << "of the student to display details: "; 
    cin >> rollnum; 

    // Read the Data from the file 
    // as String Vector 
    vector<string> row; 
    string line, word, temp; 

    while (fin >> temp) { 

        row.clear(); 

        // read an entire row and 
        // store it in a string variable 'line' 
        getline(fin, line); 

        // used for breaking words 
        stringstream s(line); 

        // read every column data of a row and 
        // store it in a string variable, 'word' 
        while (getline(s, word, ', ')) { 

            // add all the column data 
            // of a row to a vector 
            row.push_back(word); 
        } 

        // convert string to integer for comparision 
        roll2 = stoi(row[0]); 

        // Compare the roll number 
        if (roll2 == rollnum) { 

            // Print the found data 
            count = 1; 
            cout << "Details of Roll " << row[0] << " : \n"; 
            cout << "Name: " << row[1] << "\n"; 
            cout << "Maths: " << row[2] << "\n"; 
            cout << "Physics: " << row[3] << "\n"; 
            cout << "Chemistry: " << row[4] << "\n"; 
            cout << "Biology: " << row[5] << "\n"; 
            break; 
        } 
    } 
    if (count == 0) 
        cout << "Record not found\n"; 
} 


  [1]: https://i.stack.imgur.com/q6VfZ.png

1 Ответ

1 голос
/ 17 марта 2020

Я в основном сконцентрировался на добавлении перегрузок для operator<< и operator>>, поскольку вы проявили интерес к ним ранее и описали их действия в комментариях в коде.

Поскольку вы смешиваете ввод и вывод из потоков, разделенных запятыми, и других потоков. Я добавил адаптер для потоковой передачи CSV, а также перегрузки для потоковой передачи пользователю / от пользователя.

Сначала создайте class, чтобы сохранить все данные, которые принадлежат вместе в одной записи данных. Я сделал это просто struct здесь, то есть class с public доступом к своим членам по умолчанию.

#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

// your student record
struct student {
    std::string name; // It's usually good to have larger types first so name goes first
    int roll;
    int math;
    int phy;
    int chem;
    int bio;
};

// read a student from an istream (like std::cin) - whitespace separated
std::istream& operator>>(std::istream& is, student& s) {
    return is >> s.roll >> s.name >> s.math >> s.phy >> s.chem >> s.bio;
}

// write a student to an ostream (like std::cout)
std::ostream& operator<<(std::ostream& os, const student& s) {
    return os << "Details of Roll " << s.roll << ":\n"
              << "Name: " << s.name << '\n'
              << "Maths: " << s.math << '\n'
              << "Physics: " << s.phy << '\n'
              << "Chemistry: " << s.chem << '\n'
              << "Biology: " << s.bio << '\n';
}
//--------------------------------------------------------------------------------------
// An adapter for comma separated streaming
struct CSVStudent {
    CSVStudent(student& s) : stud(s) {}
    CSVStudent(const CSVStudent&) = delete;

    // The CSVStudent holds a reference to a "student"
    student& stud;
};

// read a record from an istream - comma separated
std::istream& operator>>(std::istream& is, CSVStudent& csvstud) {
    std::string line;

    student& s = csvstud.stud; // an alias to the student to have to type less

    if(std::getline(is, line)) { // read a complete line
        // put the line in an istringstream for extraction:
        std::istringstream ss(line);

        char delim; // a dummy for reading commas

        // Extract the comma separated values. "delim" is not checked so it could be
        // any char breaking up the int:s.
        //
        // The below does things in the following order:
        // 1.  "ss >> s.roll >> delim"
        //     This extracts roll and a comma and returns
        //     a reference to ss, which is used in 2.
        // 2.  std::getline(ss, s.name, ',')
        //     Extracts a string until a comma is encountered.
        // 3.  Normal extraction for the rest of the int:s with the
        //     dummy variable "delim" where the commas are supposed to be.

        if(not(std::getline(ss >> s.roll >> delim, s.name, ',') >> s.math >> delim >>
               s.phy >> delim >> s.chem >> delim >> s.bio)) {
            // If we get here, the extraction from the istringstream failed, so set
            // the failstate on the istream too. Note the "not" on the line above.
            is.setstate(std::ios::failbit);
        }
    }
    return is;
}

// write a record to an ostream - comma separated
std::ostream& operator<<(std::ostream& os, const CSVStudent& csvstud) {
    const student& s = csvstud.stud;
    os << s.roll << ',' << s.name << ',' << s.math << ',' << s.phy << ',' << s.chem
       << ',' << s.bio << '\n';
    return os;
}
//--------------------------------------------------------------------------------------
// get all students in the file as a std::vector<student>
std::vector<student> read_student_file(const std::string& filename) {
    std::vector<student> retval;
    std::ifstream fin(filename);
    if(fin) { // file opened successfully
        student stud;
        CSVStudent csvstud{stud}; // holds a reference to stud

        // loop for as long as student records can be read successfully
        while(fin >> csvstud)       // use the csv sdapter
            retval.push_back(stud); // and put the stud in the vector
    }
    return retval;
}
//--------------------------------------------------------------------------------------
void create(const std::string& filename) {
    // open an existing csv file or creates a new file.
    std::ofstream fout(filename, std::ios::out | std::ios::app);
    if(fout) {
        std::cout << "Enter the details of 5 students:"
                     " roll name maths phy chem bio\n";

        // Read the input
        for(int i = 0; i < 5; i++) {
            student stud;
            std::cout << (i + 1) << ": ";
            if(std::cin >> stud) {
                // Insert the data to file if one was entered successfully
                fout << CSVStudent(stud); // uses the adapters operator<<
            } else {
                std::cerr << "You failed to enter data for student " << (i + 1) << '\n';
                break;
            }
        }
    }
}
//--------------------------------------------------------------------------------------
int main() {
    std::string filename = "reportcard.csv";

    std::vector<student> students = read_student_file(filename);

    std::cout << "There are " << students.size() << " students in the file.\n";
    if(not students.empty()) {
        // show the last record if there are any records in the file
        std::cout << "Record " << students.size() << " is:\n\n";
        std::cout << students.back() << '\n';
    }

    // create 5 new records
    create(filename);
}

Если reportcard.csv содержит это:

1,Ted,1,2,3,4
2,Foo,2,3,4,5
3,Bar,3,4,5,6
4,Baz,4,5,6,7
5,Bork,5,6,7,8

Программа должна запуститься так:

There are 5 students in the file.
Record 5 is:

Details of Roll 5:
Name: Bork
Maths: 5
Physics: 6
Chemistry: 7
Biology: 8

Enter the details of 5 students: roll name maths phy chem bio
1: <and here is where you're supposed to enter the first of 5 new students>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...