如何检查EOF(使用getline后)

时间:2012-12-03 04:43:42

标签: c++ ifstream eof getline

请参阅下面的更新!

我的代码(现在我包含更多内容):

while(getline(checkTasks, workingString)){
            countTheDays = 0;
            // Captures a clean copy of the line in tasks.dat in it's entirety, stores in workingString.
            char nlcheck;
            checkTasks.get(nlcheck);
            if(nlcheck == '\n'){
            }
            else{
                checkTasks.unget();
                //getline(checkTasks, workingString);
                // Breaks that line up into more usable pieces of information.
                getline(check2,tName, '\t');
                getline(check2,tDate, '\t');
                getline(check2,trDate, '\t');
                getline(check2,tComplete, '\n');
                // Converts the string form of these pieces into usable integers.
                stringstream(tDate.substr(0,tDate.find_first_of('/'))) >> year;
                stringstream(tDate.substr(tDate.find_first_of('/')+1,tDate.find_last_of('/'))) >> month;
                stringstream(tDate.substr(tDate.find_last_of('/')+1,tDate.length()-1)) >> day;
                stringstream(tComplete) >> checkComplete;
                stringstream(trDate) >> checkReminder;              

                // Adds the number of days until your task is due!
                if(year != date[0]){
                    for(int i = date[1]; i <= 12; i++){
                        countTheDays += System::DateTime::DaysInMonth(date[0], i);                      
                    }
                    countTheDays-= date[2];
                    for (int i = 1; i<= month; i++){
                        countTheDays +=System::DateTime::DaysInMonth(year, i);
                    }
                    countTheDays -= (System::DateTime::DaysInMonth(year, month) - day);
                }
                else if(month != date[1]){
                    for(int i = date[1]; i <= month; i++){
                        countTheDays += System::DateTime::DaysInMonth(date[0], i);
                    }
                    countTheDays -= (date[2]);
                    countTheDays -= (System::DateTime::DaysInMonth(year, month) - day);
                }
                else{
                    countTheDays+= System::DateTime::DaysInMonth(date[0], month);
                    countTheDays -= (System::DateTime::DaysInMonth(year, month) - day);
                    countTheDays -= date[2];                    
                }

                // If the task is nearing it's due date (the time frame specified to be notified) it'll notify user. 
                // Only coded to work if the task is due in the current or following months.
                if(countTheDays <= checkReminder){
                    if( countTheDays < 0){
                        cout << endl << endl << tName << " is past due!" << endl;
                        cout << "Should I keep reminding you about this task? Enter Y or N. ";
                        cin >> continueToRemind;
                    }
                    else{
                        cout << endl << endl << tName << " is due in " <<countTheDays << " days! Don't forget!" << endl;
                        cout << "Should I keep reminding you about this task? Enter Y or N. ";
                        cin >> continueToRemind;
                        }

                    // If user doesn't want to be reminded, begins process of converting that line in the file to usable info
                    // and 'overwriting' the old file by creating a new one, deleting the old one, and renaming the new one.
                    if(continueToRemind == "n" || continueToRemind == "N"){
                        fileModified = true;
                        string line;
                    /*  vector<string> lines;
                        while(getline(tasksClone, line)){
                            lines.push_back(line);
                        }
                        lines.erase(remove(lines.begin(), lines.end(), workingString), lines.end());
                        if (!lines.empty()) {
                            auto i=lines.begin();
                            auto e=lines.end()-1;
                            for (; i!=e; ++i) {
                                saveTasks << *i << '\n';
                            }
                            saveTasks << *i;
                        }*/



                        // This writes a copy of the tasks.dat file, minus the task that the user elected not to be notified of.'
                        while(getline(tasksClone, testBuffer)){
                            if(testBuffer == workingString){
                                // This condition does nothing. Essentially erasing the 'completed' task from the list.
                            }
                            else if(testBuffer != workingString && tasksClone.eof()){
                                // This writes everything except the specified task to taskbuffer.dat
                                saveTasks << testBuffer;
                            }
                            else { 
                                saveTasks << testBuffer << '\n';
                            }
                        }                   
                    }                       
                }
            }       
        }
    }
    else{
        cout << "The tasks file is empty, you must not have any tasks!" << endl;
    }

我希望这个问题有道理!

由于

马库斯

更新:再次重写代码。告诉它删除1个任务后,它永远不会运行此循环。目前的代码:

while(getline(tasksClone, testBuffer)){
        if(testBuffer == workingString){
            // This condition does nothing. Essentially erasing the 'completed' task from the list.
        }
        else if(testBuffer != workingString && tasksClone.eof()){
            // This writes everything except the specified task to taskbuffer.dat
                saveTasks << testBuffer;
                            }
        else { 
                saveTasks << testBuffer << '\n';
        }

}

输入文件:

test1   2012/12/13  10  0;
test2   2012/12/23  20  0;
test3   2012/12/31  28  0;
\n

输出文件(在告诉它删除test1和2之后):

test2   2012/12/23  20  0;
test3   2012/12/31  28  0;
\n

5 个答案:

答案 0 :(得分:0)

我要做的第一件事就是将整个文件读入一个字符串向量,每个字符串对应一个文件中的一行。这很容易做到。

std::vector<std::string> lines;
std::string line;
while (std::getline(tasksClone, line))
    lines.push_back(line);

一旦拥有了,处理变得更加容易。您可以检查一行是否为空:

if (lines[i].empty())

这相当于检查换行符后面的下一个字符是否是另一个换行符。您可以从矢量中删除任何您不想要的行:

lines.erase(std::remove(lines.begin(), lines.end(), workingString), lines.end());

而不是那样,你可以通过在循环中放入一个读取文件中的行的测试来避免将它们放入向量中。

完成所有处理后,您可以将行重新写回另一个文件,手动插入换行符。

if (!lines.empty()) {
    auto i=lines.begin(), e=lines.end()-1;
    for (; i!=e; ++i) {
        saveTasks << *i << '\n';
    }
    saveTasks << *i;
}

答案 1 :(得分:0)

不是在每一行的末尾添加一个换行符,而只是在之后有另一行写入时,我只需每行前面换一行。这会让你在文件的开头找到新行,最后没有新行,就像你想要的那样。

像往常一样,像while (!your_file.eof())这样的代码却被打破了。你可能想要这样的东西:

while (std::getline(taskClone, buffer)) {
    if (buffer.empty()) 
        continue;

    if (keep(buffer)) // whatever condition you need
        saveTasks << "\n" << buffer;
}

您可以使用std::remove_copy_if和我的ostream_prefix_iterator更轻松地做同样的事情:

// prefix_iterator.h
#include <ostream>
#include <iterator>

template <class T, 
          class charT=char, 
          class traits=std::char_traits<charT> 
>
class prefix_ostream_iterator :
    public std::iterator<std::output_iterator_tag,void,void,void,void>
{
    charT const* delimiter;
    std::basic_ostream<charT,traits> *os;
public:
    typedef charT char_type;
    typedef traits traits_type;
    typedef std::basic_ostream<charT,traits> ostream_type;

    prefix_ostream_iterator(ostream_type& s) 
        : os(&s),delimiter(0) 
    {}
    prefix_ostream_iterator(ostream_type& s, charT const *d) 
        : os(&s),delimiter(d)
    {}
    prefix_ostream_iterator<T,charT,traits>& operator=(T const &item)
    {
        // Here's the only real change from ostream_iterator:
        // Normally, the '*os << item;' would come before the 'if'.
        if (delimiter != 0) 
            *os << delimiter;
        *os << item;
        return *this;
    }

    prefix_ostream_iterator<T,charT,traits> &operator*() {
        return *this; 
    }
    prefix_ostream_iterator<T,charT,traits> &operator++() { 
        return *this; 
    }
    prefix_ostream_iterator<T,charT,traits> &operator++(int) {
        return *this; 
    }
};

将此与我在earlier answer中发布的line代理一起使用,您可以使用以下代码执行此操作:

#include "line.h"
#include "prefix_iterator.h"

std::remove_copy_if(std::istream_iterator<line>(taskClone), 
                    std::istream_iterator<line>(),
                    prefix_ostream_iterator<std::string>(taskSave, "\n"),
                    [](std::string const &line) { /* decide if to keep line*/});

答案 2 :(得分:0)

你想要做什么并不太清楚。 std::getline 剥离尾随的新行,所以每当你输出你的内容时 读,你必须添加它。 (如果您要写入文本文件, 如果写的最后一个字符不是a,则它是未定义的行为 '\n'。)

另请注意,您的主循环不正确。的状态 tasksClone.eof()是不确定的,您无法验证 执行getline后输入成功。你是什​​么 需要是这样的:

std::string line
while ( std::getline( tasksClone, line ) ) {
    if ( line != workingString ) {
        saveTasks << line << '\n';
    }
}

在您的示例输入中,您读取的第一行将为空。

答案 3 :(得分:0)

我解决了我的问题,以防万一有人想知道。它不像它可能的内存效率,但它起作用:

// Reads the file, checks if it's due, and if it's due prompts user to either save or delete the task.
// Captures a clean copy of the line, in its entirety, and stores it to workingString.
while(getline(checkTasks,workingString)){
    countTheDays = 0;
    // Handles newline characters.
    char nlcheck;
    checkTasks.get(nlcheck);
    if(nlcheck == '\n'){
    }
    else{
        checkTasks.unget();
        // Breaks that line up into more usable pieces of information.
        getline(check2,tName, '\t');
        getline(check2,tDate, '\t');
        getline(check2,trDate, '\t');
        getline(check2,tComplete, '\n');
        // Converts the string from of these pieces into usable integers.
        stringstream(tDate.substr(0,tDate.find_first_of('/'))) >> year;                       stringstream(tDate.substr(tDate.find_first_of('/')+1,tDate.find_last_of('/'))) >> month;                  stringstream(tDate.substr(tDate.find_last_of('/')+1,tDate.length()-1)) >> day;
        stringstream(tComplete) >> checkComplete;
        stringstream(trDate) >> checkReminder;              

        // Adds the number of days until your task is due!
        if(year != date[0]){
            for(int i = date[1]; i <= 12; i++){
                countTheDays += System::DateTime::DaysInMonth(date[0], i);                      
            }
            countTheDays-= date[2];
            for (int i = 1; i<= month; i++){
                countTheDays +=System::DateTime::DaysInMonth(year, i);
            }
            countTheDays -= (System::DateTime::DaysInMonth(year, month) - day);
        }
        else if(month != date[1]){
            for(int i = date[1]; i <= month; i++){
                countTheDays += System::DateTime::DaysInMonth(date[0], i);
            }
            countTheDays -= (date[2]);
            countTheDays -= (System::DateTime::DaysInMonth(year, month) - day);
        }
        else{
            countTheDays+= System::DateTime::DaysInMonth(date[0], month);
            countTheDays -= (System::DateTime::DaysInMonth(year, month) - day);
            countTheDays -= date[2];                    
        }

        // If the task is nearing it's due date (the time frame specified to be notified) it'll notify user. 
        if(countTheDays <= checkReminder){
            if( countTheDays < 0){
                cout << endl << endl << tName << " is past due!" << endl;
                cout << "Should I keep reminding you about this task? Enter Y or N. ";
                cin >> continueToRemind;
            }
            else{
                cout << endl << endl << tName << " is due in " <<countTheDays << " days! Don't forget!" << endl;
                cout << "Should I keep reminding you about this task? Enter Y or N. ";
                cin >> continueToRemind;
            }

            // If user doesn't want to be reminded, begins process of converting that line in the file to usable info
            // and 'overwriting' the old file by creating a new one, deleting the old one, and renaming the new one.
            if(continueToRemind == "n" || continueToRemind == "N"){
                fileModified = true;
                // Adds workingString to deleteTasks[] to be compared against later.
                deleteTasks.push_back(workingString);
            }
        }
    }
}

        int match = 0;
        // Iterates through tempTasks.dat and compares lines to workingString.
        while(getline(tasksClone, testBuffer)){
            for(int i = 0; i< deleteTasks.size(); i++){
                // If a match is found, it sets the match flag to delete that line.
                if(testBuffer ==  deleteTasks[i]){
                    match = 1;
                }
            }
            // Deletes that task.
            if(match == 1){
                //Do nothing, erasing the task
            }
            // If EOF, writes only the task, without a newline character.
            else if(tasksClone.eof()){
                saveTasks << testBuffer;
            }
            // If !EOF, also writes a newline character.
            else{
                saveTasks << testBuffer << '\n';
            }
            match = 0;
        }

答案 4 :(得分:-1)

如果您在Windows或Mac中查看文件,则问题可能在于使用“\ n”。 尝试使用“\ r \ n”代替。