我需要一个流式字符串的替代方法.C++



所以我的项目是创建一个程序,该程序接受类似于以下内容的输入:

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++入门课程,所以我的知识有限,但我毫不犹豫地进行研究。正如您所看到的,我正在使用更多的入门级代码。我试着使用数组,但我得出的结论是,我仍然不知道如何成功地传递它们。

您应该将输入字符串标记化,并实现稍微复杂一点的解析。您可以在GetInput函数中使用boost::split,也可以只使用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;
}

但正确的解决方案是尝试正则表达式。在C++11中,您可以使用regex包。

您需要更好的格式规范。这两个文件看起来都像固定宽度格式的文件。

带空格的名字占据前19个字符,等级从第20位开始,每个等级占据3个字符。

你可以玩这个。

这非常详细,但演示了迭代器的使用。我使用stringstream进行演示。删除, John以查看它如何处理没有名字的情况。

注意,我从这里获取了trim,但为了简洁起见,不要在这里发布代码。

#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应该是微不足道的。

只是为了好玩,一个使用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.

相关内容

  • 没有找到相关文章

最新更新