Мне нужен альтернативный метод для потоковой передачи строк. C ++

0

Таким образом, мой проект - создать программу, которая будет вводить вход, который выглядит примерно так:

Boole, George       98  105 -1  -1  -1
Pascal, Blaise      63  48  92  92  92
Babbage, Charles    100 97  100 98  -1
Kepler, Johannes    75  102 100 -1  -1
Clown, Bozo         0   6   6   57  62
Fini, End          -99  -99 -99 -99 -99

И выведите это:

Student         Submission     Grade
Boole, George       2           105
Pascal, Blaise      3           92
Babbage, Charles    1           100
Kepler, Johannes    2           102
Clown, Bozo         5           62

У меня проблемы, потому что мой текущий код может успешно скомпилировать его, но один из моих других файлов ввода следует за другим форматом. Мой текущий код:

int main() 
{

    ifstream infile;
    ofstream outfile;
    infile.open("./ProgGrades1.txt");
    outfile.open("./GradeReporttest.txt");

    string lastName, firstName;

    int score1, score2, score3, score4, score5;
    int max, location;

    while(GetInput(infile, lastName, firstName, score1, score2, score3, score4,
            score5))
    {
        if (score1 == -99)
            break;
        AnalyzeGrade(infile, lastName, firstName, score1, score2, score3, 
               score4, score5, max, location);

        WriteOutput(infile, outfile, lastName, firstName, max, location);

        cout << lastName << " " << firstName << " " << location << " " << max <<
                endl;
    }

    infile.close();
    outfile.close();
    return 0;
}

int GetInput(ifstream& infile, string& lastName, string& firstName, int& score1,
        int& score2, int& score3, int& score4, int& score5)
{
    infile >> lastName >> firstName >> score1 >> score2 >> score3 >> 
            score4 >> score5;
    return infile;
}


int AnalyzeGrade(ifstream& infile, string& lastName, string& firstName, 
        int& score1, int& score2, int& score3, int& score4, int& score5, 
        int& max, int& location)
{
    int score[5];
    max = 0;
    score[0] = score1;
    score[1] = score2;
    score[2] = score3;
    score[3] = score4;
    score[4] = score5;

    for (int i = 0; i < 5; i++)
    {
        if (score[i] > max)
        {
            max = score[i];
        }
    }

    if (max == score[0])
    {
        location = 1;
    }
    else if (max == score[1])
    {
        location = 2;
    }
    else if (max == score[2])
    {
        location = 3;
    }
    else if (max == score[3])
    {
        location = 4;
    }
    else if (max == score[4])
    {
        location = 5;
    }
    else
    {

    }

    fill_n(score, 6, 0);
    return infile;
}

void WriteOutput(ifstream& infile, ofstream& outfile, string& lastName, 
        string& firstName, int& max, int& location)
{
    string studentID = lastName + " " + firstName;
    outfile << "\n" << setw(19) << studentID << setw(14) << location << " " << 
            max;
}

Мой другой входной файл выглядит так:

Stroustrup, Bjarne  8   8   -1  -1  -1
Lovelace, Ada       1   60  14  43  -1
von Neumann, Jon    77  48  65  -1  -1
Wirth, Niklaus      51  59  -1  -1  -1
Wozniak, Steve      81  -1  -1  -1  -1
Babbage, Charles    31  92  -1  -1  -1
Hopper, Grace       76  -1  -1  -1  -1
Bird, Tweety        -99 -99 -99 -99 -99
Sylvester           77  39  -1  -1  -1

Итак, проблема здесь в том, что мои потоки вторжения в две строки, но в строке 3 есть две части для фамилии, а для последней строки - одно имя. Мне нужен альтернативный метод для получения имен.

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

  • 0
    Мой код по существу приходит к тому же выводу. Используя переменные lastName и firstName, я могу получить полное имя и вывести его в виде одной строки. В строке 3 есть запятая, но фамилия состоит из двух частей, так что это может привести к путанице.
  • 0
    std::getline(infile, ',') будет извлекать все до первой запятой и сбрасывать запятую. Может быть, вы могли бы использовать это.
Показать ещё 1 комментарий
Теги:
string
input
stream

4 ответа

1
Лучший ответ

Вы должны обозначить свою входную строку и реализовать немного более совершенный синтаксический анализ. Вы можете использовать boost :: split в своей функции GetInput или только функцию strtok. Как это:

int GetInput(ifstream& infile, string& lastName, string& firstName, int& score1,
        int& score2, int& score3, int& score4, int& score5)
{
    std::string line = infile.str ();
    std::list<std::string> tokens; // or something fancy with boost::iterator_range

    boost::split (tokens, line, boost::is_any_of(",")); // define your own predicate if needed
    // check result and tokens collection before processing it
    std::list<std::string>::iterator it = tokens.begin();
    lastName.swap(*it++);
    // now you should split rightmost token the same way but with space between tokens...

    return infile;
}

Но правильное решение будет пытаться использовать регулярные выражения. В мире С++ 11 вы можете использовать пакет regex.

1

Вам нужна более качественная спецификация формата. Оба ваших файла выглядят как файлы с фиксированной шириной.

Имена с пробелами занимают первые 19 символов, оценки начинаются с 20-го положения, каждый класс занимает 3 символа.

Вы можете играть на этом.

0

Просто для удовольствия, программа, использующая boost::spirit которая выполняет эту работу. Конечно, есть более чистый способ обработки строк.

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/home/phoenix/object/construct.hpp>
#include <boost/spirit/home/phoenix/container.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/optional.hpp>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>

struct Student
{
    boost::optional<std::string> first_name;
    std::string last_name;
    std::vector<signed long> grades;

    bool fill(const std::string& str)
    {
        namespace qi = boost::spirit::qi;
        namespace ascii = boost::spirit::ascii;
        namespace phoenix = boost::phoenix;

        typedef std::vector<char> chars;
        auto set_last_name = 
            [this](const std::vector<char>& name)
            {
                last_name = std::string(name.begin(), name.end());
            };
        auto set_first_name = 
            [this](const std::vector<char>& name)
            {
                first_name = std::string(name.begin(), name.end());
            };

        bool r = qi::phrase_parse(str.begin(), str.end(),
            (
                (+qi::alpha)[ set_last_name ] // gives vector of char
                >> -(',' >> +qi::alpha)[ set_first_name ] // gives vector of char
                >> *(qi::int_ [ phoenix::push_back(phoenix::ref(grades), qi::_1) ])
            ), qi::space);    

        return r;
    }
};


int main(int argc, char* argv[])
{    
    if (argc < 2)
    {
        std::cout << "Please specify a filename" << std::endl;
        return -1;
    }

    std::ifstream file(argv[1]);

    if (!file)
    {
         std::cout << "Invalid filename: " << argv[1] << std::endl;   
         return -2;
    }

    std::vector<Student> students;
    std::string str;
    while (getline(file, str))
    {       
        Student student;
        if (student.fill(str))
        {
            std::cout << "Parsing succeeded, adding '" ;

            if (student.first_name)
            {
                 std::cout << *student.first_name << " ";
            }

            std::cout 
                << student.last_name 
                << "' with " << student.grades.size() << " grades." 
                << std::endl;
            students.push_back(student);
        }
        else
        {
            std::cout << "Parsing failed." << std::endl;
        }
    }

    return 0;
}

И вот вывод:

$ ./a.exe input.txt
Parsing succeeded, adding 'Bjarne Stroustrup' with 5 grades.
Parsing succeeded, adding 'Ada Lovelace' with 5 grades.
Parsing succeeded, adding 'Jon vonNeumann' with 5 grades.
Parsing succeeded, adding 'Niklaus Wirth' with 5 grades.
Parsing succeeded, adding 'Steve Wozniak' with 5 grades.
Parsing succeeded, adding 'Charles Babbage' with 5 grades.
Parsing succeeded, adding 'Grace Hopper' with 5 grades.
Parsing succeeded, adding 'Tweety Bird' with 5 grades.
Parsing succeeded, adding 'Sylvester' with 5 grades.
0

Это очень многословно, но демонстрирует использование итераторов. Я использую stringstream для демонстрационных целей. Удалите , John чтобы увидеть, как он не имеет никакого имени.

Заметьте, я убираю утилизацию здесь, но не размещайте здесь код для краткости.

#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <cctype>

int main() {
  std::string line = "von Neumann, John    77  48  65  -1  -1";
  std::istringstream iss(line);
  auto it = std::find(line.begin(), line.end(), ',');
  std::string last_name;
  std::string first_name;
  std::string integer_list;

  // If we didn't find a comma
  if (it == line.end())
  {
      // We have a last name only
      first_name = "NO_FIRST_NAME";
      // No comma, so we just search up to the first integer
      auto find_integer_it = std::find_if(line.begin(), line.end(), [] (char c) { return isdigit(c); });
      last_name = std::string(line.begin(), find_integer_it);
      // Get rest of string from the position of first integer
      integer_list = std::string(find_integer_it, line.end());
      trim(last_name);
  } else {
    last_name = std::string(line.begin(), it);

    // it+2 because we're skipping the comma
    // and the whitespace after the comma
    auto space_it = std::find(it+2, line.end(), ' ');
    first_name = std::string(it+2, space_it);
    auto find_integer_it = std::find_if(line.begin(), line.end(), [] (char c) { return isdigit(c); });
    integer_list = std::string(find_integer_it, line.end());
  }
  std::cout << last_name << ", " << first_name << std::endl;
  std::cout << integer_list << std::endl;
}

Вывод:

von Neumann, John

77  48  65  -1  -1

На данный момент должно быть тривиально разобрать integer_list.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню