简体   繁体   English

如何检查EOF(使用getline之后)

[英]How to check if EOF (after using getline)

See update below! 请参阅下面的更新!

My code (now I included more): 我的代码(现在我包括了更多):

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;
    }

I hope that question makes sense! 我希望这个问题有道理!

Thanks 谢谢

Marcus 马库斯

UPDATE: Reworked the code, again. 更新:重新编写代码。 After telling it to delete 1 task, it never runs this loop. 告诉它删除1个任务后,它将永远不会运行此循环。 Current code: 当前代码:

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';
        }

} }

Input file: 输入文件:

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

Output file (after telling it to delete test1 and 2): 输出文件(告诉它删除test1和2后):

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

The first thing I would do is just read your whole file into a vector of strings, each string corresponding to a line in the file. 我要做的第一件事就是将整个文件读入一个字符串向量中,每个字符串对应于文件中的一行。 That can be done quite easily. 这很容易做到。

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

Once you have that, processing becomes much easier. 一旦有了这些,处理就变得容易得多。 You can check if a line is empty like this: 您可以像这样检查行是否为空:

if (lines[i].empty())

That's equivalent to checking if the next character after a newline is another newline. 这等效于检查换行符之后的下一个字符是否为另一个换行符。 You can remove any lines from the vector that you don't want like this: 您可以像这样从向量中删除不需要的任何行:

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

Instead of that though, you could avoid putting them into the vector in the first place by putting a test in the loop that reads the lines from the file. 但是,相反,您可以通过将测试放在从文件中读取行的循环中来避免将它们放在向量的第一位。

Once you've done all your processing, you can just write the lines back out to the other file, inserting the newlines manually. 完成所有处理后,您只需将这些行写回到另一个文件中,即可手动插入换行符。

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

Instead of adding a new-line at the end of each line, but only if there's another line to write afterwards, I'd just precede each line by a new-line. 而不是在每行的末尾添加换行符,而是仅在随后要写另一行时,我才每行之前加上换行符。 That'll get you the new-line at the beginning of the file, and no new-line at the end, just as you want. 这将使您在文件的开头获得换行符,而在结尾处没有换行符。

As usual, code like while (!your_file.eof()) is broken though. 与往常一样, while (!your_file.eof())类的代码已损坏。 You probably want something like: 您可能想要类似的东西:

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

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

You can do the same thing a little more easily with std::remove_copy_if and my ostream_prefix_iterator : 您可以使用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; 
    }
};

Using this along with a line proxy I posted in an earlier answer , you could do the job with code something like: 将此与我在较早的答案中发布的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*/});

It's not too clear what you are trying to do. 目前尚不清楚您要做什么。 std::getline strips the trailing new line, so any time you output what you've read, you have to add it. std::getline会删除尾随的新行,因此,每当您输出已阅读的内容时,都必须添加它。 (If you're writing to a text file, it's undefined behavior if the last character written isn't a '\\n' .) (如果要写入文本文件,则最后写入的字符不是'\\n' ,这是未定义的行为。)

Note too that your main loop is incorrect. 还要注意,您的主循环是不正确的。 The state of tasksClone.eof() is indeterminate, and you don't verify that the input has succeeded after doing the getline . tasksClone.eof()的状态是不确定的,在执行getline之后,您不会验证输入是否成功。 What you need is something like: 您需要的是这样的:

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

In your example input, the first line you read will be empty. 在示例输入中,您读取的第一行将为空。

I solved my problem, just in case anyone wants to know. 我解决了我的问题,以防万一有人想知道。 It's not as memory efficient as it could be, but it functions: 它的内存效率不尽如人意,但可以发挥作用:

// 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;
        }

You problem may be in using "\\n" if you are viewing your files in Windows or Mac. 如果您在Windows或Mac中查看文件,则可能是使用“ \\ n”引起的。 Try to use "\\r\\n" instead. 尝试改用“ \\ r \\ n”。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM