Почему я получаю пустой вывод после написания этого кода для обработки файлов в C ++? - PullRequest
0 голосов
/ 21 марта 2020

Я создал класс тестера, где я беру вопросы из текстового файла пула вопросов и помещаю оттуда случайные вопросы в файл docx. Я хочу знать, почему мой код выдает пустой вывод в файле docx. моя случайная функция работает нормально. Я выбираю два двух вопроса из трех файлов. Вот мой код - `

void test()
{
  string line;
  fstream question1("questiondesc.txt",ios::in | ios::out | ios::app);
  fstream testgen("GeneratedTest.docx",ios::trunc | ios::in | ios::out);
  testgen.open("GeneratedTest.docx");
  if(!question1.is_open())
  {
    question1.open("questiondesc.txt");
  }
  int i,num; 
  for (i = 0; i < 2; i++) { 
    num = random(1,12);
    for(int i =1;i<=num;i++)
    {
        getline(question1,line);
    }
    question1.clear();
    question1.seekg(0, ios::beg);
    testgen<<line<<endl;
  }
  question1.close();
  ifstream question2("questionmcq.txt");
  if(!question2.is_open())
  {
    question2.open("questionmcq.txt");
  }
  for (i = 0; i < 2; i++) {
    num = random(1,26);
    while(num%2==0)
    { 
    num = random(1,26);
    }
    for(int i =1;i<=num;i++)
    {
      getline(question2,line);
    }
    testgen<<line<<endl;
    getline(question2,line);
    testgen<<line<<endl;
    question2.clear();
    question2.seekg(0, ios::beg);
  }
  question2.close();
  ifstream question3("questionanalytical.txt");
  if(!question3.is_open())
  {
    question3.open("questionanalytical.txt");
  }
  for (i = 0; i < 2; i++) { 
    num = random(1,12);
    for(int i =1;i<=num;i++)
    {
      getline(question3,line);
    }
    question3.clear();
    question3.seekg(0, ios::beg);
    testgen<<line<<endl;
  }
  question3.close();
  testgen.close();
}

1 Ответ

0 голосов
/ 21 марта 2020

В вашем коде есть ошибки. Я покажу их как комментарий в списке ниже. Кроме того, я покажу (один из многих, и, возможно, не самые лучшие) решения для вашей проблемы.

Вы должны разбить вашу проблему на более мелкие части и разработать больше функций. Тогда жизнь станет проще.

Дополнительно. Вы должны написать комментарии. Если вы пишете комментарии, то вы сами обнаружите проблемы.

Ваш код с моими замечаниями:

#include <iostream>
#include <fstream>
#include <string>
#include <random>

using namespace std;  // NO NEVER USE

int random(int from, int to) {
    std::random_device                  random_device;
    std::mt19937                        generator(random_device());
    std::uniform_int_distribution<int>  distribution(from, to);
    return distribution(generator);
}

void test()
{
    string line;    // Line is not initialized an not needed here. Pollutes namespace
    fstream question1("questiondesc.txt", ios::in | ios::out | ios::app);  // Opening a file with these flags will fail. Use ifstream
    fstream testgen("GeneratedTest.docx", ios::trunc | ios::in | ios::out);// Opening a file with these flags will fail. Use ofstream
    testgen.open("GeneratedTest.docx"); // File was alread opened and failed. Reopening will not work. It failed alread
    if (!question1.is_open()) // Use if "(!question1)" instead. There could be also other error bits
    {   // Always check the status of any IO operation
        question1.open("questiondesc.txt"); // Will never work. Failer already
    }
    int i, num; // Variable not initialized and not needed here. Name space pollution
    for (i = 0; i < 2; i++) {
        num = random(1, 12); // This function was not defined. I redefined it
        for (int i = 1; i <= num; i++)  // i=1 and i<= reaaly) not i=0 and i<num?
        {
            getline(question1, line);   // Always check status of any IO function
        }
        question1.clear();
        question1.seekg(0, ios::beg);
        testgen << line << endl;
    }
    question1.close(); // The destructor of the fstream will close the file for you

    ifstream question2("questionmcq.txt");  // Now you open the file as ifstream
    if (!question2.is_open())  // Do check for all possible flags.: If (!question2)
    {
        question2.open("questionmcq.txt");  // Will not work, if it failed in the first time
    }
    for (i = 0; i < 2; i++) {   // So 2 times
        num = random(1, 26);
        while (num % 2 == 0) // If numbers are equal
        {
            num = random(1, 26); // Get an odd number
        }
        for (int i = 1; i <= num; i++) // Usually from 0 to <num
        {
            getline(question2, line);
        }
        testgen << line << endl;
        getline(question2, line);
        testgen << line << endl;
        question2.clear();
        question2.seekg(0, ios::beg);
}
    question2.close();   // No need to close. Destructor will do it for you
    ifstream question3("questionanalytical.txt");    // Now you open the file as ifstream
    if (!question3.is_open())  // Wrong check. Check for all flags
    {
        question3.open("questionanalytical.txt"); // Will not help in case of failure
    }
    // Now this is the 3rd time with the same code. So, put it into a function
    for (i = 0; i < 2; i++) {
        num = random(1, 12);
        for (int i = 1; i <= num; i++)
        {
            getline(question3, line);
        }
        question3.clear();
        question3.seekg(0, ios::beg);
        testgen << line << endl;
    }
    question3.close();
    testgen.close();
}

int main() {
    test();
    return 0;
}

И вот одно из возможных решений. С функциями для обработки похожих частей кода:

#include <iostream>
#include <string>
#include <fstream>
#include <random>
#include <vector>
#include <tuple>

// From the internet: https://en.cppreference.com/w/cpp/numeric/random/random_device
int random(int from, int to) {
    std::random_device                  random_device;
    std::mt19937                        generator(random_device());
    std::uniform_int_distribution<int>  distribution(from, to);
    return distribution(generator);
}

std::string readNthLineFromFile(std::ifstream& ifs, int n) {

    // Reset file to the beginning
    ifs.clear();
    ifs.seekg(0, std::ios::beg);

    // Default return string in case of error
    std::string result{ "\n*** Error while reading a line from the source file\n" };

    // If getline fails or ifs is in fail state, the string will be default
    for (; std::getline(ifs, result) && (n != 0); n--);

    // Give back the desired line
    return result;
}

void generateQuestion(std::ifstream& sourceFileStream, std::ofstream& destinationFileStream, int n, const bool twoLines = false) {

    // We want to prevent readin the same question again
    int oldLineNumber = 0;
    // For whatever reason, do this 2 times. 
    for (size_t i = 0U; i < 2; ++i) {



        // If we want to read 2 consecutive lines, then we should not come up with the last kine in the file
        if (twoLines & (n > 1)) --n;

        // Get a random line number. But no duplicates in the 2 loops
        int lineNumber{};
        do {
            lineNumber = random(1, n);
        } while (lineNumber == oldLineNumber);

        // For the next loop execution
        oldLineNumber = lineNumber;

        // Read the random line
        std::string line{ readNthLineFromFile(sourceFileStream, lineNumber) };

        // And write it to the destination file
        destinationFileStream << line << "\n";

        // If we want to read to lines in a row
        if (twoLines) {
            // Read next line
            line = readNthLineFromFile(sourceFileStream, ++lineNumber);

            // And write it to the destination file
            destinationFileStream << line << "\n";
        }
    }
}

int main() {

    const std::string destinationFilename{ "generatedTest.txt" };
    const std::string questions1Filename{ "questiondesc.txt" };
    const std::string questions2Filename{ "questionmcq.txt" };
    const std::string questions3Filename{ "questionanalytical.txt" };

    // Here we store the filenames and if one or 2 lines shall be read
    std::vector<std::tuple<const std::string, const size_t, const bool>> source{
        { questions1Filename, 12U, false },
        { questions2Filename, 26U, true },
        { questions3Filename, 12U, false }
    };
    // Open the destination file and check, if it could be opened
    if (std::ofstream destinationFileStream(destinationFilename); destinationFileStream) {

        // Now open the first source file and generate the questions
        for (const std::tuple<const std::string, const size_t, const bool>& t : source) {

            // Open source file and check, if it could be opened 
            if (std::ifstream sourceFileStream(std::get<0>(t)); sourceFileStream) {

                generateQuestion(sourceFileStream, destinationFileStream, std::get<1>(t), std::get<2>(t));
            }
            else {
                std::cerr << "\n*** Error. Could not open source file '" << std::get<0>(t) << "'\n";
            }
        }
    }
    else {
        std::cerr << "\n*** Error: Could not open destination file '" << destinationFilename << "'\n";
    }

    return 0;
}
...