【问题标题】:I need an alternate method to streaming strings. C++我需要另一种流式传输字符串的方法。 C++
【发布时间】:2013-12-07 00:35:21
【问题描述】:

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

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++ 课程,所以我的知识有限,但我对研究毫无疑虑。如您所见,我正在使用更多的入门级代码。尝试使用数组,但得出结论还是不明白如何成功传递。

【问题讨论】:

  • 我的代码基本上得出了相同的结论。使用变量 lastName 和 firstName,我可以获取整个名称并将其作为一个字符串输出。第 3 行有逗号,但姓氏有两部分,所以会搞砸。
  • std::getline(infile, ',') 将提取到第一个逗号的所有内容并删除逗号。也许你可以使用它。
  • 这适用于第三行,但不适用于最后一行,因为没有逗号可停。

标签: c++ string input stream


【解决方案1】:

您应该标记您的输入字符串并实现稍微复杂的解析。您可以在 GetInput 函数或 strtok 函数中使用 boost::split。像这样:

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 包。

【讨论】:

    【解决方案2】:

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

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

    你可以在上面玩。

    【讨论】:

      【解决方案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应该是微不足道的。

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-06-16
          • 1970-01-01
          • 2021-05-24
          • 1970-01-01
          • 1970-01-01
          • 2022-11-23
          • 2016-02-02
          相关资源
          最近更新 更多