我需要另一种方法来流式传输字符串。 C ++

时间:2013-11-22 07:44:17

标签: c++ string input stream

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

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

所以这里的问题是我的infile以两个字符串流,但在第3行,姓氏有两个部分,而最后一行有一个名字。我需要另一种方法来获取名称。

顺便说一下,我目前正在参加C ++课程的介绍,所以我的知识有限,但我没有疑虑研究。如您所见,我正在使用更多入门级代码。我试图使用数组,但我得出结论,我仍然不明白如何成功传递它们。

4 个答案:

答案 0 :(得分:1)

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

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

你可以玩。

答案 1 :(得分:1)

您应该对输入字符串进行标记,并实现稍微复杂的解析。您可以在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世界中,您可以使用正则表达式包。

答案 2 :(得分:0)

这非常详细,但演示了迭代器的使用。我使用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应该是微不足道的。

答案 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.