C++ 在将新联系人添加到联系人列表时,如何确保文件在程序运行之间不会被覆盖?

C++ How do I make sure file is not overwritten between program runs when adding new contacts to a contact list?

提问人:arghe 提问时间:8/22/2023 最后编辑:user207421arghe 更新时间:8/22/2023 访问量:54

问:

我正在尝试为一个简单的通讯录创建一个程序,该程序从用户那里获取有关联系人的信息并将其保存在文件中。当我重新运行程序并下次打开相同的文件时,我显然希望我之前添加的所有联系人仍然存在,并且如果我选择添加新联系人(或者只是退出程序而不添加任何新联系人),则不会被覆盖。 但是我无法正确保存文件。我可以将联系人添加到地图中,但根据我放置 saveToFile() 的位置,文件总是在某个时候覆盖联系人,无论是在添加新联系人之后还是在程序运行之间。

这是我的通讯录和 main 函数调用的类:

#include <iostream>
#include <fstream>
#include <map>
#include <sstream>

struct Contact {
   std::string name;
   std::string address;
   std::string email;
   std::string number;
   std::string bday;
   std::string other;
}

class ContactBook { 
   private:
   std::map<std::string, Contact> contacts; 

   public:
   void ContactBook::loadFromFile(const std::string filename) {
        std::ifstream file(filename);
        if (!file) {
            std::cout << "Unable to open file" << std::endl;
            return;
        }  
        
        std::string line;
        while (std::getline(file, line)) {
            std::istringstream iss(line);
            Contact contact;
            if (std::getline(iss, contact.name, '\n') &&
                std::getline(iss, contact.address, '\n') &&
                std::getline(iss, contact.email, '\n') &&
                std::getline(iss, contact.number, '\n') &&
                std::getline(iss, contact.bday, '\n') &&
                std::getline(iss, contact.other, '\n')) {
                    contacts[contact.name] = contact;
                }
        }
        file.close();
        std::cout << "File " << filename << " loaded" << std::endl;
    }
    
  void ContactBook::addContact(const Contact& contact) {
    contacts[contact.name] = contact;   
  } 

  void ContactBook::saveToFile(const std::string& filename) {
    std::ofstream file;
    file.open(filename);
    for (const auto& pair : contacts) {
        const Contact& contact = pair.second;
        file << contact.name <<  '\n' <<
            contact.address << '\n' <<
            contact.email << '\n' <<
            contact.number << '\n' <<
            contact.bday << '\n' <<
            contact.other << '\n';
            
    } file.close();
  }

 void ContactBook::printFile(const std::string& filename) {
    std::string line;
    std::ifstream file;
    file.open(filename);
    if (file.is_open()) {
        while(std::getline(file, line)) {
            std::cout << line << '\n';
        }
        file.close();
    }
  }
}


int main() {

    ContactBook contactBook;
    contactBook.load("contacts.txt");
    int alt;
    bool exit = false;
    
    while (!exit) {
    std::cout << "1 - Add contact" << '\n' <<
             "2 - Remove contact" << '\n' <<   //will add later
             "3 - Search for contact" << '\n' << //will add later
             "4 - Save and exit" << '\n' <<
             "5 - Print contact list" << std::endl;
    
    std::cin >> alt;
    
    switch(alt) {
        case 1: {Contact contact;
            std::cin.ignore();  
            std::cout << "Name: ";
            std::getline(std::cin, contact.name);
            std::cout << "Address: ";
            std::getline(std::cin, contact.address);
            std::cout << "Email: ";
            std::getline(std::cin, contact.email);
            std::cout << "Phone number: ";
            std::getline(std::cin, contact.number);
            std::cout << "Birthday: ";
            std::getline(std::cin, contact.bday);
            std::cout << "Other info: ";
            std::getline(std::cin, contact.other);
            
            contactBook.addContact(contact);
            }
            break;
        case 2: /*...*/
            break;
        case 3: /*..*/
            break;
        case 4: contactBook.saveToFile("contacts.txt");
            exit = true;
            break;
        case 5: contactBook.printFile("contacts.txt");
            break;
        default: std::cout << "invalid input" << std::endl;
    }
  }
  return 0;
}

截至目前,如果我添加联系人,保存并重新运行程序并选择打印,则之前添加的联系人将被正确打印。但是,如果我第二次保存并退出,那么下次运行文件将是空的。 如果在同一程序运行期间添加另一个联系人,第一个联系人也将消失 - 如果我保存并退出,文件将仅包含第二个联系人。

对不起,如果我在这里犯了一个超级愚蠢和明显的错误——我是初学者。但我就是想不通,我已经被困了几个小时,以至于我无法再真正专注于我正在做的事情,非常感谢。

C++ IOstream

评论

0赞 user207421 8/22/2023
这与哎呀用户交互完全无关。这是关于iostream的。不要不分青红皂白地标记。
0赞 M.M 8/22/2023
稳健地执行此操作的一种方法是使用 SQLite 来管理文件

答:

3赞 Remy Lebeau 8/22/2023 #1

您读取文件的方式永远无法工作,因为默认情况下,当它遇到 时会停止读取,因此它永远无法输出包含 a 的文件。但是您期望多个字段位于用 分隔的单行上,这是无稽之谈。std::getline()'\n'std::string'\n''\n'

循环的第一次迭代将仅将第一个联系人读入 ,然后后续迭代将无法读入其余值,因为它们在 中不存在。然后,下一次迭代将把第一个联系人读入 ,随后将失败。等等。whilenameistringstreamifistringstreamwhileaddressistringstreamif

要执行您正在尝试的操作,请改用其他分隔符,例如 ,例如:'\t'

void ContactBook::loadFromFile(const std::string filename) {
    std::ifstream file(filename);
    if (!file) {
        std::cout << "Unable to open file" << std::endl;
        return;
    }  
        
    std::string line;
    while (std::getline(file, line)) {
        std::istringstream iss(line);
        Contact contact;
        if (std::getline(iss, contact.name, '\t') &&
            std::getline(iss, contact.address, '\t') &&
            std::getline(iss, contact.email, '\t') &&
            std::getline(iss, contact.number, '\t') &&
            std::getline(iss, contact.bday, '\t') &&
            std::getline(iss, contact.other))
        {
            contacts[contact.name] = contact;
        }
    }
    file.close();
    std::cout << "File " << filename << " loaded" << std::endl;
}
    
void ContactBook::saveToFile(const std::string& filename) {
    std::ofstream file(filename);
    if (!file) {
        std::cout << "Unable to create file" << std::endl;
        return;
    }  

    for (const auto& pair : contacts) {
        const Contact& contact = pair.second;
        file << contact.name <<  '\t' <<
                contact.address << '\t' <<
                contact.email << '\t' <<
                contact.number << '\t' <<
                contact.bday << '\t' <<
                contact.other << '\n';            
    }
    file.close();
    std::cout << "File " << filename << " saved" << std::endl;
}

否则,如果你想用来分隔你的字段,那么你需要去掉 ,直接读取行,例如:'\n'std::istringstreamstd::ifstream

void ContactBook::loadFromFile(const std::string filename) {
    std::ifstream file(filename);
    if (!file) {
        std::cout << "Unable to open file" << std::endl;
        return;
    }  
        
    Contact contact;
    while (std::getline(file, contact.name) &&
           std::getline(file, contact.address) &&
           std::getline(file, contact.email) &&
           std::getline(file, contact.number) &&
           std::getline(file, contact.bday) &&
           std::getline(file, contact.other))
    {
        contacts[contact.name] = contact;
    }
    file.close();
    std::cout << "File " << filename << " loaded" << std::endl;
}
    
void ContactBook::saveToFile(const std::string& filename) {
    std::ofstream file(filename);
    if (!file) {
        std::cout << "Unable to create file" << std::endl;
        return;
    }  

    for (const auto& pair : contacts) {
        const Contact& contact = pair.second;
        file << contact.name <<  '\n' <<
                contact.address << '\n' <<
                contact.email << '\n' <<
                contact.number << '\n' <<
                contact.bday << '\n' <<
                contact.other << '\n';
    }
    file.close();
    std::cout << "File " << filename << " saved" << std::endl;
}

评论

0赞 arghe 8/22/2023
答案是肯定的!简直不敢相信我错过了。谢谢你,你是救命恩人!